This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new b02536e4 Revert "[Rust][Fix] Preserve object lifetime and 
thread-safety contracts" (#772)
b02536e4 is described below

commit b02536e4f3804e2e41dacf2ad2d52da805f46d90
Author: Shushi Hong <[email protected]>
AuthorDate: Wed Sep 9 16:51:47 2026 -0400

    Revert "[Rust][Fix] Preserve object lifetime and thread-safety contracts" 
(#772)
    
    Reverts apache/tvm-ffi#766
---
 examples/rust_stubgen/README.md                   |  34 +---
 python/tvm_ffi/stub/cli.py                        |  12 +-
 python/tvm_ffi/stub/generator.py                  |   9 +-
 python/tvm_ffi/stub/python_generator/generator.py |   1 -
 python/tvm_ffi/stub/rust_generator/codegen.py     |  14 +-
 python/tvm_ffi/stub/rust_generator/consts.py      |  18 +-
 python/tvm_ffi/stub/rust_generator/directives.py  |  10 +-
 python/tvm_ffi/stub/rust_generator/generator.py   |   8 +-
 rust/tvm-ffi/src/function.rs                      |  42 +----
 rust/tvm-ffi/src/object.rs                        | 138 +++++++-------
 rust/tvm-ffi/tests/test_function.rs               |  26 ---
 rust/tvm-ffi/tests/test_object.rs                 |  76 --------
 tests/python/test_stubgen_rust.py                 | 214 ----------------------
 13 files changed, 89 insertions(+), 513 deletions(-)

diff --git a/examples/rust_stubgen/README.md b/examples/rust_stubgen/README.md
index 12670a17..35906b31 100644
--- a/examples/rust_stubgen/README.md
+++ b/examples/rust_stubgen/README.md
@@ -111,42 +111,12 @@ Besides `prefix` and `custom-new`, this example declares 
the integer field
 // tvm-ffi-stubgen(enum): rust_stubgen.IntPair.kind -> PairKind(i32) { 
Unordered=0, Ordered=1 }
 ```
 
-`field` names the Rust type of a field
+Three more are available: `field` names the Rust type of a field
 (`// tvm-ffi-stubgen(field): rust_stubgen.IntPair.a -> MyInt`), `nullable`
-wraps an object-reference field in `Option` (for example, a nullable `span`),
+wraps it in `Option` (`// tvm-ffi-stubgen(nullable): rust_stubgen.IntPair.a`),
 and `upcast` adds a conversion to a hand-written typed view
 (`// tvm-ffi-stubgen(upcast): rust_stubgen.IntPair -> MyView`).
 
-## Construction and ownership
-
-A complete layout does not always permit direct allocation. For a 
registry-owned
-type, keep its readable fields but suppress both generated allocators:
-
-```rust
-// tvm-ffi-stubgen(no-alloc): <type_key>
-```
-
-`no-alloc` takes precedence over `custom-new` and also applies to descendants.
-`no-alloc`, `nullable`, and `opaque` are shared across all input files in the 
same
-invocation, so include the files declaring these policies when generating
-descendants. Directives containing Rust type names remain file-local. The 
binding
-supplies the registry lookup; stubgen does not infer native constructor 
semantics.
-
-Layout metadata and pointer sizes describe the loaded libraries and the current
-process. Run stubgen against the ABI you intend to build for, not a different
-cross-compilation target.
-
-## Thread safety
-
-Complete and opaque objects inherit `!Send` and `!Sync` from `tvm_ffi::Object`,
-including when viewed through a base or `ObjectRef`. The zero-sized marker does
-not change their ABI layout. `Function` remains shareable, so its `from_packed`
-and `from_typed` callbacks require `Send + Sync` captures. Scoped structural
-callbacks are unaffected. `ObjectArc` requires unique ownership for mutable 
access.
-
-These are source-compatibility changes. Any unsafe thread-safety opt-in must
-cover hidden native state, destruction, and all accepted dynamic subtypes.
-
 ## Partial generation
 
 Every type an object refers to (its parent, its ancestors, the types of its
diff --git a/python/tvm_ffi/stub/cli.py b/python/tvm_ffi/stub/cli.py
index 83776827..af8f8c24 100644
--- a/python/tvm_ffi/stub/cli.py
+++ b/python/tvm_ffi/stub/cli.py
@@ -39,7 +39,7 @@ from .lib_state import (
 from .utils import FuncInfo, InitConfig, Options
 
 if TYPE_CHECKING:
-    from collections.abc import Container, Sequence
+    from collections.abc import Container
 
     from .generator import Generator
 
@@ -110,12 +110,6 @@ def __main__() -> int:
         for code in file.code_blocks
         if code.kind == "object" and isinstance(code.param, str)
     )
-    shared_directives = [
-        code
-        for file in files
-        for code in file.code_blocks
-        if code.kind == "directive" and code.param[0] in 
generator.shared_directive_kinds
-    ]
     for file in files:
         if opt.verbose:
             print(f"{C.TERM_CYAN}[File] {file.path}{C.TERM_RESET}")
@@ -127,7 +121,6 @@ def __main__() -> int:
                 global_funcs,
                 generator=generator,
                 declared=declared,
-                shared_directives=shared_directives,
             )
         except Exception:
             failed += 1
@@ -329,14 +322,13 @@ def _stage_3(  # noqa: PLR0912
     global_funcs: dict[str, list[FuncInfo]],
     generator: Generator,
     declared: Container[str] = frozenset(),
-    shared_directives: Sequence[CodeBlock] = (),
 ) -> bool:
     """Process one file's blocks; return whether its content is (or would be) 
changed."""
     defined_funcs: set[str] = set()
     defined_types: set[str] = set()
     imports = generator.new_imports()
     # Stage 1. Hand the one-line directives the pipeline does not consume 
itself to the generator.
-    for code in [*shared_directives, *file.code_blocks]:
+    for code in file.code_blocks:
         if code.kind != "directive":
             continue
         name, payload = code.param
diff --git a/python/tvm_ffi/stub/generator.py b/python/tvm_ffi/stub/generator.py
index 47182397..c22522a4 100644
--- a/python/tvm_ffi/stub/generator.py
+++ b/python/tvm_ffi/stub/generator.py
@@ -82,10 +82,6 @@ class Generator(Protocol):
     #: to the pipeline; any other undeclared name is an error.
     directive_kinds: frozenset[str]
 
-    #: Type policies that also apply to references/descendants in other input 
files.
-    #: Unlike local type spellings, these directives must not depend on Rust 
imports.
-    shared_directive_kinds: frozenset[str]
-
     def default_ty_map(self) -> dict[str, str]:
         """Return the default FFI-origin -> target-type name map for this 
language."""
         ...
@@ -99,9 +95,8 @@ class Generator(Protocol):
     def add_directive(self, imports: Any, name: str, payload: str, lineno: 
int) -> None:
         """Record a one-line directive (raw payload) into ``imports``.
 
-        The collector is per file. The pipeline also seeds it with directives
-        in :attr:`shared_directive_kinds` from the other input files.
-        ``name`` is always one of :attr:`directive_kinds`;
+        The collector is per file, so a directive applies to the blocks of the
+        file it appears in. ``name`` is always one of :attr:`directive_kinds`;
         the payload's grammar is the generator's to define.
         """
         ...
diff --git a/python/tvm_ffi/stub/python_generator/generator.py 
b/python/tvm_ffi/stub/python_generator/generator.py
index d2f46089..5d784818 100644
--- a/python/tvm_ffi/stub/python_generator/generator.py
+++ b/python/tvm_ffi/stub/python_generator/generator.py
@@ -46,7 +46,6 @@ class PythonGenerator:
     syntax = C.PYTHON_SYNTAX
     source_exts = frozenset({".py", ".pyi"})
     directive_kinds: frozenset[str] = frozenset({"import-object"})
-    shared_directive_kinds: frozenset[str] = frozenset()
 
     def default_ty_map(self) -> dict[str, str]:
         """Return the default FFI-origin -> Python-type name map."""
diff --git a/python/tvm_ffi/stub/rust_generator/codegen.py 
b/python/tvm_ffi/stub/rust_generator/codegen.py
index 0eca3783..7385dab7 100644
--- a/python/tvm_ffi/stub/rust_generator/codegen.py
+++ b/python/tvm_ffi/stub/rust_generator/codegen.py
@@ -41,10 +41,6 @@ provided in the same run: ``ffi.*`` by the crate, a 
``ty-map`` by a hand-written
 binding whose object struct is ``<Name>Obj``, anything else by an ``object/``
 block in one of the processed files. Otherwise the block is an error naming the
 missing keys, so a partial binding never references a module that does not 
exist.
-
-Layout and allocation are separate: ``no-alloc`` suppresses both allocators
-without hiding readable fields. Thread restrictions are inherited from the
-crate's ``Object`` base, including for opaque views.
 """
 
 from __future__ import annotations
@@ -263,7 +259,11 @@ class _ObjectRenderer:
             payload.origin in C_RUST.RUST_ANY_BACKED_OPTIONAL_PAYLOADS
             or payload.origin == "Optional"
         )
-        expected = C_RUST.RUST_OPTIONAL_FIELD_SIZE if any_backed else 
C_RUST.RUST_POINTER_SIZE
+        expected = (
+            C_RUST.RUST_OPTIONAL_FIELD_SIZE
+            if any_backed
+            else C_RUST.RUST_OBJECT_OPTIONAL_FIELD_SIZE
+        )
         if field.size not in (None, expected):
             return None
         if any_backed:
@@ -534,9 +534,7 @@ class _ObjectRenderer:
                 ]
             )
         elif verdict.is_complete:
-            hierarchy = {self.type_key, *self.info.ancestors}
-            if not hierarchy.intersection(self.imports.directives.no_alloc):
-                sections += self._allocator_sections(base, has_parent)
+            sections += self._allocator_sections(base, has_parent)
         upcasts = self._upcast_lines()
         if upcasts:
             sections.append(upcasts)
diff --git a/python/tvm_ffi/stub/rust_generator/consts.py 
b/python/tvm_ffi/stub/rust_generator/consts.py
index eb85701d..b6543dbf 100644
--- a/python/tvm_ffi/stub/rust_generator/consts.py
+++ b/python/tvm_ffi/stub/rust_generator/consts.py
@@ -18,20 +18,9 @@
 
 from __future__ import annotations
 
-import ctypes
-
 #: One-line directives the Rust backend consumes.
 RUST_DIRECTIVE_KINDS = frozenset(
-    {
-        "import-object",
-        "field",
-        "nullable",
-        "enum",
-        "opaque",
-        "upcast",
-        "custom-new",
-        "no-alloc",
-    }
+    {"import-object", "field", "nullable", "enum", "opaque", "upcast", 
"custom-new"}
 )
 
 #: Default FFI-origin -> Rust-type map; ``::`` paths get a ``use``, bare names 
do not.
@@ -92,12 +81,13 @@ RUST_SCALAR_WIDTHS = {
     "f64": 8,
 }
 
-#: The registry is loaded in this process, so its object pointers have the 
host ABI size.
-RUST_POINTER_SIZE = ctypes.sizeof(ctypes.c_void_p)
+#: Size of an object reference field; ``nullable`` may only wrap those.
+RUST_POINTER_SIZE = 8
 
 #: C++ ``Optional<T>`` is a 16-byte ``TVMFFIAny`` cell, or a nullable pointer 
for object payloads.
 RUST_OPTIONAL_PATH = "tvm_ffi::Optional"
 RUST_OPTIONAL_FIELD_SIZE = 16
+RUST_OBJECT_OPTIONAL_FIELD_SIZE = 8
 
 #: ``Optional`` payloads kept as the 16-byte cell (a nested ``Optional`` too); 
the size is checked.
 RUST_ANY_BACKED_OPTIONAL_PAYLOADS = frozenset(
diff --git a/python/tvm_ffi/stub/rust_generator/directives.py 
b/python/tvm_ffi/stub/rust_generator/directives.py
index e7fd262a..185b9065 100644
--- a/python/tvm_ffi/stub/rust_generator/directives.py
+++ b/python/tvm_ffi/stub/rust_generator/directives.py
@@ -16,7 +16,7 @@
 # under the License.
 """The Rust backend's one-line directives: payload grammar and per-file 
storage.
 
-Field directives address ``<type_key>.<field>``; type directives address 
``<type_key>``::
+Three address one reflected field as ``<type_key>.<field>``, three address a 
type::
 
     // tvm-ffi-stubgen(field): tirx.Add.a -> PrimExpr
     // tvm-ffi-stubgen(nullable): ir.Expr.span
@@ -24,7 +24,6 @@ Field directives address ``<type_key>.<field>``; type 
directives address ``<type
     // tvm-ffi-stubgen(opaque): ir.SourceName
     // tvm-ffi-stubgen(upcast): tirx.Add -> PrimExpr
     // tvm-ffi-stubgen(custom-new): tirx.Add
-    // tvm-ffi-stubgen(no-alloc): ir.SourceName
 
 ``field`` sets the field's Rust type (a name in scope, or a ``::`` path to
 ``use``); on a field inherited from an ancestor it narrows the allocator
@@ -33,10 +32,6 @@ integer newtype for it; ``opaque`` keeps a type opaque even 
when its layout is
 reproducible. ``upcast`` adds a typed view outside the ancestor chain;
 ``custom-new`` says the wrapper's ``new`` is hand-written; the generated one is
 named ``from_complete_fields`` instead.
-
-``no-alloc`` preserves readable fields but suppresses both allocators, 
including
-in descendants. Like ``nullable`` and ``opaque``, it applies across all files 
in
-a generation run. Directives containing Rust type names remain file-local.
 """
 
 from __future__ import annotations
@@ -70,7 +65,6 @@ class Directives:
     opaque: set[str] = dataclasses.field(default_factory=set)
     upcasts: dict[str, list[str]] = dataclasses.field(default_factory=dict)
     custom_new: set[str] = dataclasses.field(default_factory=set)
-    no_alloc: set[str] = dataclasses.field(default_factory=set)
 
     def add(self, name: str, payload: str, lineno: int) -> None:
         """Parse and store one directive; raise ``ValueError`` on a malformed 
payload."""
@@ -89,8 +83,6 @@ class Directives:
             self.upcasts.setdefault(_type_target(name, lhs, lineno), 
[]).append(rust_type)
         elif name == "custom-new":
             self.custom_new.add(_type_target(name, payload, lineno))
-        elif name == "no-alloc":
-            self.no_alloc.add(_type_target(name, payload, lineno))
         else:
             raise ValueError(f"Unknown directive `{name}` at line {lineno}")
 
diff --git a/python/tvm_ffi/stub/rust_generator/generator.py 
b/python/tvm_ffi/stub/rust_generator/generator.py
index 20b72b59..edfc7c07 100644
--- a/python/tvm_ffi/stub/rust_generator/generator.py
+++ b/python/tvm_ffi/stub/rust_generator/generator.py
@@ -25,7 +25,6 @@ from __future__ import annotations
 from typing import TYPE_CHECKING
 
 from .. import consts as C
-from ..lib_state import object_info_from_type_key
 from . import consts as C_RUST
 from .codegen import (
     finalize_rust_module_tree,
@@ -44,13 +43,12 @@ if TYPE_CHECKING:
 
 
 class RustGenerator:
-    """Generator for complete and opaque Rust bindings (see 
:mod:`.codegen`)."""
+    """Generator that emits opaque Rust bindings for reflected objects (see 
:mod:`.codegen`)."""
 
     name = "rust"
     syntax = C.RUST_SYNTAX
     source_exts = frozenset({".rs"})
     directive_kinds = C_RUST.RUST_DIRECTIVE_KINDS
-    shared_directive_kinds = frozenset({"no-alloc", "nullable", "opaque"})
 
     def default_ty_map(self) -> dict[str, str]:
         """Return the default FFI-origin -> Rust-type name map."""
@@ -68,8 +66,6 @@ class RustGenerator:
             imports.record(payload.split(";", 1)[0].strip())
         else:
             imports.directives.add(name, payload, lineno)
-            if name == "no-alloc":
-                object_info_from_type_key(payload.strip())
 
     def canonical_type_name(self, type_key: str) -> str:
         """Return the Rust path for a defined type key (matches 
:attr:`RustUse.path`)."""
@@ -104,7 +100,7 @@ class RustGenerator:
         obj_info: ObjectInfo,
         declared: Container[str] = frozenset(),
     ) -> None:
-        """Emit the Rust binding for an ``object/<key>`` block."""
+        """Emit the opaque Rust binding for an ``object/<key>`` block."""
         generate_rust_object(code, ty_map, imports, opt, obj_info, declared)
 
     def generate_import_section_block(
diff --git a/rust/tvm-ffi/src/function.rs b/rust/tvm-ffi/src/function.rs
index a6bcd5b7..d87e72a9 100644
--- a/rust/tvm-ffi/src/function.rs
+++ b/rust/tvm-ffi/src/function.rs
@@ -38,18 +38,12 @@ pub struct FunctionObj {
     cell: TVMFFIFunctionCell,
 }
 
-/// A shareable packed function. Rust callbacks must have thread-safe captures.
+/// Error reference class
 #[derive(Clone, ObjectRef)]
 pub struct Function {
     data: ObjectArc<FunctionObj>,
 }
 
-// SAFETY: Rust callbacks require Send + Sync, including their captured state.
-// Foreign callbacks must uphold the same contract (see from_extern_c). Opt in
-// only the handle, not FunctionObj or its potentially stateful derived 
layouts.
-unsafe impl Send for Function {}
-unsafe impl Sync for Function {}
-
 //------------------------------------------------------------------------
 // CallbackFunctionObjImpl
 //------------------------------------------------------------------------
@@ -317,18 +311,7 @@ impl Function {
             Ok(())
         }
     }
-    /// Construct a function from a packed function.
-    ///
-    /// A Function can be called, retained, and dropped on another thread, so 
its
-    /// captured state must be `Send + Sync`. This does not require its 
arguments
-    /// or result to be `Send`: they are supplied and returned on the calling 
thread.
-    ///
-    /// ```compile_fail
-    /// use std::rc::Rc;
-    /// use tvm_ffi::{Any, Function};
-    /// let state = Rc::new(1i64);
-    /// let _ = Function::from_packed(move |_| Ok(Any::from(*state)));
-    /// ```
+    /// Construct a function from a packed function
     /// # Arguments
     /// * `func` - The packed function in signature of `Fn(&[AnyView]) -> 
Result<Any>`
     ///
@@ -336,7 +319,7 @@ impl Function {
     /// * `Function` - The function
     pub fn from_packed<F>(func: F) -> Self
     where
-        F: Fn(&[AnyView]) -> Result<Any> + Send + Sync + 'static,
+        F: Fn(&[AnyView]) -> Result<Any> + 'static,
     {
         unsafe {
             let callback_arc = 
ObjectArc::new(CallbackFunctionObjImpl::from_callback(func));
@@ -347,19 +330,7 @@ impl Function {
         }
     }
 
-    /// Construct a function from a typed function.
-    ///
-    /// Captured state must be `Send + Sync`, as for [`Self::from_packed`].
-    ///
-    /// ```compile_fail
-    /// use std::cell::Cell;
-    /// use tvm_ffi::Function;
-    /// let state = Cell::new(0i64); // Send, but not Sync.
-    /// let _ = Function::from_typed(move || {
-    ///     state.set(state.get() + 1);
-    ///     Ok(state.get())
-    /// });
-    /// ```
+    /// Construct a function from a typed function
     /// # Arguments
     /// * `func` - The typed function with function signature of `F(T0, T1, 
...) -> Result<O>`
     ///
@@ -367,7 +338,7 @@ impl Function {
     /// * `Function` - The function
     pub fn from_typed<F, I, O>(func: F) -> Self
     where
-        F: AsPackedCallable<I, O> + Send + Sync + 'static,
+        F: AsPackedCallable<I, O> + 'static,
     {
         let closure = move |packed_args: &[AnyView]| -> Result<Any> {
             let ret_value = func.call_packed(packed_args)?;
@@ -381,9 +352,6 @@ impl Function {
     /// `handle` must be a valid pointer (or null) that is compatible with
     /// `safe_call` and `deleter`. The caller must ensure the handle outlives
     /// the returned `Function` (or that `deleter` properly frees it).
-    /// `safe_call` must support concurrent calls from arbitrary threads, and
-    /// `deleter` must be safe to run on any thread. These requirements also 
apply
-    /// to the state behind `handle`.
     pub unsafe fn from_extern_c(
         handle: *mut std::ffi::c_void,
         safe_call: TVMFFISafeCallType,
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index 2619817a..d690a64b 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -25,24 +25,14 @@ pub use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
 /// Object related ABI handling
 use tvm_ffi_sys::{TVMFFIAny, TVMFFIGetTypeInfo, TVMFFIObject, 
COMBINED_REF_COUNT_BOTH_ONE};
 
-/// The common object header, including for objects with unknown native state.
-///
-/// Objects are not `Send` or `Sync` by default: atomic reference counting does
-/// not make an object's fields or destructor thread-safe. Keeping this marker
-/// in the base also prevents casts to a base node or [`ObjectRef`] from 
erasing
-/// a derived object's thread restrictions. It does not change the C ABI 
layout.
-///
-/// An explicit unsafe `Send`/`Sync` implementation for a binding must account 
for
-/// every dynamic subtype it accepts, including hidden state and destruction.
+/// Object type is by default the TVMFFIObject
 #[repr(C)]
 pub struct Object {
+    /// example implementation of the object
     header: TVMFFIObject,
-    _thread_confined: std::marker::PhantomData<std::rc::Rc<()>>,
 }
 
-/// Arc-like wrapper for Object that allows shared ownership.
-///
-/// Mutable dereferencing panics if another strong or external weak owner 
exists.
+/// Arc-like wrapper for Object that allows shared ownership
 ///
 /// \tparam T The type of the object to be wrapped
 #[repr(C)]
@@ -54,19 +44,6 @@ pub struct ObjectArc<T: ObjectCore> {
 unsafe impl<T: Send + Sync + ObjectCore> Send for ObjectArc<T> {}
 unsafe impl<T: Send + Sync + ObjectCore> Sync for ObjectArc<T> {}
 
-// The allocation length must outlive T's destructor when weak owners remain.
-// Keep it before the ABI-visible object, never in the live reference-count 
header.
-fn extra_items_layout<T: ObjectCoreWithExtraItems>(count: usize) -> 
(std::alloc::Layout, usize) {
-    use std::alloc::Layout;
-    let items = Layout::array::<T::ExtraItem>(count).expect("extra items 
layout overflow");
-    let (body, _) = Layout::new::<T>()
-        .extend(items)
-        .expect("object layout overflow");
-    Layout::new::<usize>()
-        .extend(body)
-        .expect("allocation layout overflow")
-}
-
 /// Traits that can be used to check if a type is an object
 ///
 /// This trait is unsafe because it is used to access the object header
@@ -363,7 +340,7 @@ pub mod unsafe_ {
     /// * `obj` - The object to increase the reference count
     #[inline]
     pub unsafe fn inc_ref(handle: *mut TVMFFIObject) {
-        let obj = &*handle;
+        let obj = &mut *handle;
         obj.combined_ref_count.fetch_add(1, Ordering::Relaxed);
     }
 
@@ -375,16 +352,17 @@ pub mod unsafe_ {
     /// * `obj` - The object to decrease the reference count
     #[inline]
     pub(crate) unsafe fn dec_ref(handle: *mut TVMFFIObject) {
-        // Do not keep a Rust reference to the header across a deleter call.
-        let old_combined_count = (*handle)
+        let obj = &mut *handle;
+        let old_combined_count = obj
             .combined_ref_count
-            // Match C++ Object::DecRef: publish this owner's writes before the
-            // last owner acquires them and runs the destructor.
-            .fetch_sub(COMBINED_REF_COUNT_STRONG_ONE, Ordering::Release);
+            .fetch_sub(COMBINED_REF_COUNT_STRONG_ONE, Ordering::Relaxed);
         if old_combined_count == COMBINED_REF_COUNT_BOTH_ONE {
-            fence(Ordering::Acquire);
-            if let Some(deleter) = (*handle).deleter {
-                deleter(handle.cast(), kTVMFFIObjectDeleterFlagBitMaskBoth as 
i32);
+            if let Some(deleter) = obj.deleter {
+                fence(Ordering::Acquire);
+                deleter(
+                    obj as *mut TVMFFIObject as *mut c_void,
+                    kTVMFFIObjectDeleterFlagBitMaskBoth as i32,
+                );
             }
         } else if (old_combined_count & COMBINED_REF_COUNT_MASK_U32)
             == COMBINED_REF_COUNT_STRONG_ONE
@@ -392,16 +370,22 @@ pub mod unsafe_ {
             // slow path, there is still a weak reference left
             // need to run two phase decrement
             fence(Ordering::Acquire);
-            if let Some(deleter) = (*handle).deleter {
-                deleter(handle.cast(), kTVMFFIObjectDeleterFlagBitMaskStrong 
as i32);
+            if let Some(deleter) = obj.deleter {
+                deleter(
+                    obj as *mut TVMFFIObject as *mut c_void,
+                    kTVMFFIObjectDeleterFlagBitMaskStrong as i32,
+                );
             }
-            let old_weak_count = (*handle)
+            let old_weak_count = obj
                 .combined_ref_count
                 .fetch_sub(COMBINED_REF_COUNT_WEAK_ONE, Ordering::Release);
             if old_weak_count == COMBINED_REF_COUNT_WEAK_ONE {
                 fence(Ordering::Acquire);
-                if let Some(deleter) = (*handle).deleter {
-                    deleter(handle.cast(), kTVMFFIObjectDeleterFlagBitMaskWeak 
as i32);
+                if let Some(deleter) = obj.deleter {
+                    deleter(
+                        obj as *mut TVMFFIObject as *mut c_void,
+                        kTVMFFIObjectDeleterFlagBitMaskWeak as i32,
+                    );
                 }
             }
         }
@@ -409,13 +393,13 @@ pub mod unsafe_ {
 
     #[inline]
     pub(crate) unsafe fn strong_count(handle: *mut TVMFFIObject) -> usize {
-        let obj = &*handle;
+        let obj = &mut *handle;
         (obj.combined_ref_count.load(Ordering::Relaxed) & 
COMBINED_REF_COUNT_MASK_U32) as usize
     }
 
     #[inline]
     pub(crate) unsafe fn weak_count(handle: *mut TVMFFIObject) -> usize {
-        let obj = &*handle;
+        let obj = &mut *handle;
         (obj.combined_ref_count.load(Ordering::Relaxed) >> 32) as usize
     }
 
@@ -440,16 +424,31 @@ pub mod unsafe_ {
         T: super::ObjectCoreWithExtraItems<ExtraItem = U>,
     {
         let obj = ptr as *mut T;
-        if flags & kTVMFFIObjectDeleterFlagBitMaskStrong as i32 != 0 {
+        if flags == kTVMFFIObjectDeleterFlagBitMaskBoth as i32 {
+            let extra_items_count = T::extra_items_count(&(*obj));
             std::ptr::drop_in_place(obj);
-        }
-        if flags & kTVMFFIObjectDeleterFlagBitMaskWeak as i32 != 0 {
-            // The object's offset depends only on alignment, not item count.
-            let (_, offset) = super::extra_items_layout::<T>(0);
-            let allocation = ptr.cast::<u8>().sub(offset);
-            let count = allocation.cast::<usize>().read();
-            let (layout, _) = super::extra_items_layout::<T>(count);
-            std::alloc::dealloc(allocation, layout);
+            let layout = std::alloc::Layout::from_size_align(
+                std::mem::size_of::<T>() + extra_items_count * 
std::mem::size_of::<U>(),
+                std::mem::align_of::<T>(),
+            )
+            .unwrap();
+            std::alloc::dealloc(ptr as *mut u8, layout);
+        } else {
+            assert_eq!(std::mem::size_of::<T>() % std::mem::size_of::<u64>(), 
0);
+            if flags & kTVMFFIObjectDeleterFlagBitMaskStrong as i32 != 0 {
+                let extra_items_count = T::extra_items_count(&(*obj));
+                std::ptr::drop_in_place(obj);
+                std::ptr::write(obj as *mut u64, extra_items_count as u64);
+            }
+            if flags & kTVMFFIObjectDeleterFlagBitMaskWeak as i32 != 0 {
+                let extra_items_count = std::ptr::read(obj as *mut u64) as 
usize;
+                let layout = std::alloc::Layout::from_size_align(
+                    std::mem::size_of::<T>() + extra_items_count * 
std::mem::size_of::<U>(),
+                    std::mem::align_of::<T>(),
+                )
+                .unwrap();
+                std::alloc::dealloc(ptr as *mut u8, layout);
+            }
         }
     }
 }
@@ -462,7 +461,6 @@ impl Object {
     pub fn new() -> Self {
         Self {
             header: TVMFFIObject::new(),
-            _thread_confined: std::marker::PhantomData,
         }
     }
 }
@@ -521,13 +519,16 @@ impl<T: ObjectCore> ObjectArc<T> {
             assert_eq!(std::mem::align_of::<T>() % std::mem::align_of::<U>(), 
0);
             assert_eq!(std::mem::size_of::<T>() % std::mem::align_of::<U>(), 
0);
             let extra_items_count = T::extra_items_count(&data);
-            let (layout, offset) = extra_items_layout::<T>(extra_items_count);
+            let layout = std::alloc::Layout::from_size_align(
+                std::mem::size_of::<T>() + extra_items_count * 
std::mem::size_of::<U>(),
+                std::mem::align_of::<T>(),
+            )
+            .unwrap();
             let raw_data_ptr = std::alloc::alloc(layout);
             if raw_data_ptr.is_null() {
                 std::alloc::handle_alloc_error(layout);
             }
-            raw_data_ptr.cast::<usize>().write(extra_items_count);
-            let ptr = raw_data_ptr.add(offset).cast::<T>();
+            let ptr = raw_data_ptr as *mut T;
             std::ptr::write(ptr, data);
             // now override the header directly
             std::ptr::write(
@@ -602,7 +603,7 @@ impl<T: ObjectCore> ObjectArc<T> {
     /// * `*mut T` - The raw pointer
     #[inline]
     pub unsafe fn as_raw_mut(this: &mut Self) -> *mut T {
-        this.ptr.as_ptr()
+        this.ptr.as_mut()
     }
 
     /// Get the strong reference count of the ObjectArc
@@ -614,7 +615,9 @@ impl<T: ObjectCore> ObjectArc<T> {
     /// * `usize` - The strong reference count
     #[inline]
     pub fn strong_count(this: &Self) -> usize {
-        unsafe { 
unsafe_::strong_count(this.ptr.as_ptr().cast::<TVMFFIObject>()) }
+        unsafe {
+            unsafe_::strong_count(this.ptr.as_ref() as *const T as *mut T as 
*mut TVMFFIObject)
+        }
     }
 
     /// Get the weak reference count of the ObjectArc
@@ -626,7 +629,7 @@ impl<T: ObjectCore> ObjectArc<T> {
     /// * `usize` - The weak reference count
     #[inline]
     pub fn weak_count(this: &Self) -> usize {
-        unsafe { unsafe_::weak_count(this.ptr.as_ptr().cast::<TVMFFIObject>()) 
}
+        unsafe { unsafe_::weak_count(this.ptr.as_ref() as *const T as *mut T 
as *mut TVMFFIObject) }
     }
 }
 
@@ -639,29 +642,18 @@ impl<T: ObjectCore> Deref for ObjectArc<T> {
     }
 }
 
-// Exclusive Rust access requires both a single strong owner and no external
-// weak owners that could acquire another strong reference during the borrow.
+// implement DerefMut for ObjectArc
 impl<T: ObjectCore> DerefMut for ObjectArc<T> {
     #[inline]
     fn deref_mut(&mut self) -> &mut Self::Target {
-        unsafe {
-            let header = &*self.ptr.as_ptr().cast::<TVMFFIObject>();
-            assert_eq!(
-                header
-                    .combined_ref_count
-                    .load(std::sync::atomic::Ordering::Acquire),
-                COMBINED_REF_COUNT_BOTH_ONE,
-                "cannot mutably borrow a shared ObjectArc"
-            );
-            self.ptr.as_mut()
-        }
+        unsafe { self.ptr.as_mut() }
     }
 }
 
 // implement Drop for ObjectArc
 impl<T: ObjectCore> Drop for ObjectArc<T> {
     fn drop(&mut self) {
-        unsafe { unsafe_::dec_ref(self.ptr.as_ptr().cast::<TVMFFIObject>()) }
+        unsafe { unsafe_::dec_ref(self.ptr.as_mut() as *mut T as *mut 
TVMFFIObject) }
     }
 }
 
@@ -669,7 +661,7 @@ impl<T: ObjectCore> Drop for ObjectArc<T> {
 impl<T: ObjectCore> Clone for ObjectArc<T> {
     #[inline]
     fn clone(&self) -> Self {
-        unsafe { unsafe_::inc_ref(self.ptr.as_ptr().cast::<TVMFFIObject>()) }
+        unsafe { unsafe_::inc_ref(self.ptr.as_ref() as *const T as *mut T as 
*mut TVMFFIObject) }
         Self {
             ptr: self.ptr,
             _phantom: std::marker::PhantomData,
diff --git a/rust/tvm-ffi/tests/test_function.rs 
b/rust/tvm-ffi/tests/test_function.rs
index ccf2e9cb..07ed6779 100644
--- a/rust/tvm-ffi/tests/test_function.rs
+++ b/rust/tvm-ffi/tests/test_function.rs
@@ -53,32 +53,6 @@ fn test_function_from_packed() {
     assert_eq!(i32::try_from(result).unwrap(), 6);
 }
 
-#[test]
-fn test_function_thread_safe_captures_and_global_cache() {
-    use std::sync::atomic::{AtomicUsize, Ordering};
-    use std::sync::Arc;
-
-    let calls = Arc::new(AtomicUsize::new(0));
-    let state = calls.clone();
-    let function = Function::from_typed(move |value: i64| {
-        state.fetch_add(1, Ordering::Relaxed);
-        // Neither the argument holder nor the returned Any needs to be Send.
-        cached_global_func!("testing.echo").call_tuple((value,))
-    });
-    std::thread::scope(|scope| {
-        for value in 0..4i64 {
-            let function = &function;
-            scope.spawn(move || {
-                let result: i64 = 
function.call_tuple((value,)).unwrap().try_into().unwrap();
-                assert_eq!(result, value);
-            });
-        }
-    });
-    assert_eq!(calls.load(Ordering::Relaxed), 4);
-    std::thread::spawn(move || drop(function)).join().unwrap();
-    assert_eq!(Arc::strong_count(&calls), 1);
-}
-
 #[test]
 fn test_function_from_typed() {
     let offset = 2;
diff --git a/rust/tvm-ffi/tests/test_object.rs 
b/rust/tvm-ffi/tests/test_object.rs
index 54b3ccd1..ebab18e0 100644
--- a/rust/tvm-ffi/tests/test_object.rs
+++ b/rust/tvm-ffi/tests/test_object.rs
@@ -21,34 +21,6 @@ use std::sync::Arc;
 use std::{collections::HashMap, hash::Hash};
 use tvm_ffi::*;
 
-// An erased owning or borrowed view must not acquire thread-safety merely
-// because its concrete object's non-thread-safe fields are no longer visible.
-macro_rules! assert_not_impl {
-    ($ty:ty: $bound:path) => {
-        const _: fn() = || {
-            trait AmbiguousIfImpl<A> {
-                fn check() {}
-            }
-            impl<T: ?Sized> AmbiguousIfImpl<()> for T {}
-            struct ImplementsBound;
-            impl<T: ?Sized + $bound> AmbiguousIfImpl<ImplementsBound> for T {}
-            let _ = <$ty as AmbiguousIfImpl<_>>::check;
-        };
-    };
-}
-
-assert_not_impl!(Object: Send);
-assert_not_impl!(Object: Sync);
-assert_not_impl!(ObjectArc<Object>: Send);
-assert_not_impl!(ObjectArc<Object>: Sync);
-assert_not_impl!(tvm_ffi::object::ObjectRef: Send);
-assert_not_impl!(tvm_ffi::object::ObjectRef: Sync);
-assert_not_impl!(ObjectIdentity: Send);
-assert_not_impl!(ObjectIdentity: Sync);
-assert_not_impl!(&Object: Send);
-assert_not_impl!(Any: Send);
-assert_not_impl!(AnyView<'static>: Send);
-
 // must have repr(C) for the object header stays in the same position
 #[repr(C)]
 struct TestIntObj {
@@ -128,21 +100,6 @@ fn test_object_arc() {
     assert_eq!(delete_counter.load(Ordering::Relaxed), 1);
 }
 
-#[test]
-fn test_object_arc_mutable_borrow_requires_unique_ownership() {
-    let deleted = Arc::new(AtomicU32::new(0));
-    let mut value = ObjectArc::new(TestIntObj::new(1, deleted, 0));
-    let alias = value.clone();
-    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
-        value.value = 2;
-    }));
-    assert!(result.is_err());
-    assert_eq!(alias.value, 1);
-    drop(alias);
-    value.value = 3;
-    assert_eq!(value.value, 3);
-}
-
 #[test]
 fn test_object_arc_with_extra_items() {
     let delete_counter = Arc::new(AtomicU32::new(0));
@@ -166,39 +123,6 @@ fn test_object_arc_with_extra_items() {
     assert_eq!(delete_counter.load(Ordering::Relaxed), 1);
 }
 
-#[test]
-fn test_extra_items_with_a_surviving_weak_owner() {
-    use tvm_ffi::tvm_ffi_sys::{
-        TVMFFIObjectDeleterFlagBitMask::kTVMFFIObjectDeleterFlagBitMaskWeak,
-        COMBINED_REF_COUNT_WEAK_ONE,
-    };
-    for count in [0, 3] {
-        let deleted = Arc::new(AtomicU32::new(0));
-        let value = ObjectArc::new_with_extra_items(TestIntObj::new(1, 
deleted.clone(), count));
-        // Model a native weak reference through the existing combined-count 
ABI.
-        unsafe {
-            let header = ObjectArc::as_raw(&value).cast::<TVMFFIObject>();
-            (*header)
-                .combined_ref_count
-                .fetch_add(COMBINED_REF_COUNT_WEAK_ONE, Ordering::Relaxed);
-            drop(value);
-            assert_eq!(deleted.load(Ordering::Relaxed), 1);
-            assert_eq!(
-                (*header)
-                    .combined_ref_count
-                    .fetch_sub(COMBINED_REF_COUNT_WEAK_ONE, Ordering::Release),
-                COMBINED_REF_COUNT_WEAK_ONE,
-            );
-            std::sync::atomic::fence(Ordering::Acquire);
-            (*header).deleter.unwrap()(
-                header.cast_mut().cast(),
-                kTVMFFIObjectDeleterFlagBitMaskWeak as i32,
-            );
-        }
-        assert_eq!(deleted.load(Ordering::Relaxed), 1);
-    }
-}
-
 #[test]
 fn test_object_arc_from_raw() {
     unsafe {
diff --git a/tests/python/test_stubgen_rust.py 
b/tests/python/test_stubgen_rust.py
index 35474ad8..05449b8c 100644
--- a/tests/python/test_stubgen_rust.py
+++ b/tests/python/test_stubgen_rust.py
@@ -18,12 +18,7 @@
 
 from __future__ import annotations
 
-import ctypes
-import os
 import re
-import shutil
-import subprocess
-import sys
 from collections.abc import Container, Iterator
 from pathlib import Path
 
@@ -31,7 +26,6 @@ import pytest
 import tvm_ffi.stub.cli as stub_cli
 import tvm_ffi.testing  # noqa: F401  (loads the `testing.*` fixture types)
 from tvm_ffi.core import TypeSchema
-from tvm_ffi.libinfo import find_libtvm_ffi
 from tvm_ffi.stub import consts as C
 from tvm_ffi.stub.cli import _stage_1, _stage_3
 from tvm_ffi.stub.file_utils import CodeBlock, FileInfo
@@ -247,7 +241,6 @@ def test_directives_parse() -> None:
     directives.add("upcast", "tirx.Add -> PrimExpr", 6)
     directives.add("upcast", "tirx.Add -> crate::typed::TypedExpr", 7)
     directives.add("custom-new", " tirx.Add ", 8)
-    directives.add("no-alloc", " ir.SourceName ", 9)
     assert directives.field_types == {"tirx.Add.a": "PrimExpr"}
     assert directives.nullable == {"ir.Expr.span"}
     assert directives.enums == {
@@ -257,7 +250,6 @@ def test_directives_parse() -> None:
     assert directives.opaque == {"ir.SourceName"}
     assert directives.upcasts == {"tirx.Add": ["PrimExpr", 
"crate::typed::TypedExpr"]}
     assert directives.custom_new == {"tirx.Add"}
-    assert directives.no_alloc == {"ir.SourceName"}
 
 
 @pytest.mark.parametrize(
@@ -292,7 +284,6 @@ def 
test_generator_declares_its_directives_and_records_imports() -> None:
         "opaque",
         "upcast",
         "custom-new",
-        "no-alloc",
     }
     imports = RUST.new_imports()
     RUST.add_directive(imports, "import-object", 
"tvm_ffi.libinfo.Foo;False;_Foo", 1)
@@ -890,33 +881,6 @@ def test_complete_optional_field_mirrors() -> None:
     assert "tvm_ffi::Optional" in _uses(imports)
 
 
[email protected]("pointer_size", [4, 8])
-def test_object_reference_fields_use_native_pointer_size(
-    monkeypatch: pytest.MonkeyPatch, pointer_size: int
-) -> None:
-    assert RC.RUST_POINTER_SIZE == ctypes.sizeof(ctypes.c_void_p)
-    monkeypatch.setattr(RC, "RUST_POINTER_SIZE", pointer_size)
-    info = _info(
-        "demo.References",
-        (
-            _field("value", "Object", 24, pointer_size),
-            _field(
-                "optional",
-                TypeSchema("Optional", (TypeSchema("Object"),)),
-                24 + pointer_size,
-                pointer_size,
-            ),
-        ),
-        total_size=24 + 2 * pointer_size,
-    )
-    imports = RustImports()
-    RUST.add_directive(imports, "nullable", "demo.References.value", 1)
-    text, _ = _render(info, imports)
-    assert "/// Complete:" in text
-    assert "pub value: Option<ObjectRef>," in text
-    assert "pub optional: Option<ObjectRef>," in text
-
-
 @pytest.mark.parametrize(
     "field",
     [
@@ -957,22 +921,6 @@ def test_custom_new_renames_the_wrapper_allocator() -> 
None:
     assert "    pub fn new(" not in text
 
 
-def test_no_alloc_preserves_fields_and_applies_to_descendants() -> None:
-    base = _info("demo.Base", (_field("value", "Object", 24, 8),), 
total_size=32)
-    child = _info("demo.Child", parent="demo.Base", total_size=32)
-    _register(base)
-    imports = RustImports()
-    for name in ("no-alloc", "custom-new"):
-        imports.directives.add(name, "demo.Base", 1)
-    for info in (base, child):
-        text, _ = _render(info, imports)
-        assert "/// Complete:" in text
-        assert "fn new(" not in text
-        assert "from_complete_fields" not in text
-    text, _ = _render(base, imports)
-    assert "pub value: ObjectRef," in text
-
-
 def test_upcast_directive_adds_typed_views() -> None:
     """`upcast` targets follow the ancestor chain; a `::` path is imported. 
Opaque: no allocator."""
     info = _info("demo.Leaf", parent="demo.Base")
@@ -1173,168 +1121,6 @@ def 
test_stage_3_applies_directives_to_a_registered_type(tmp_path: Path) -> None
     assert "use tvm_ffi::Error;" in text
 
 
-def test_cli_shares_object_policies_across_files(
-    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
-) -> None:
-    # The child lives in a separate file; it must not bypass the base's 
allocation policy.
-    for filename, key, directives in (
-        ("base.rs", "testing.TestCxxClassBase", ["no-alloc"]),
-        ("child.rs", "testing.TestCxxClassDerived", []),
-    ):
-        (tmp_path / filename).write_text(
-            "\n".join(
-                [f"{C.RUST_SYNTAX.directive(name)} {key}" for name in 
directives]
-                + [f"{C.RUST_SYNTAX.begin} object/{key}", C.RUST_SYNTAX.end, 
""]
-            ),
-            encoding="utf-8",
-        )
-    monkeypatch.setattr("sys.argv", ["tvm-ffi-stubgen", "--target", "rust", 
str(tmp_path)])
-    assert stub_cli.__main__() == 0
-    for filename in ("base.rs", "child.rs"):
-        text = (tmp_path / filename).read_text(encoding="utf-8")
-        assert "/// Complete:" in text
-        assert "fn new(" not in text
-    assert stub_cli.__main__() == 0
-
-
[email protected]("policy", ["nullable", "opaque"])
-def test_cli_preserves_parent_layout_policies_across_files(
-    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, policy: str
-) -> None:
-    base = _info("demo.Base", (_field("value", "Object", 24, 8),), 
total_size=32)
-    child = _info("demo.Child", parent="demo.Base", total_size=32)
-    _register(base, child)
-    monkeypatch.setattr(stub_cli, "object_info_from_type_key", 
codegen.object_info_from_type_key)
-    target = "demo.Base.value" if policy == "nullable" else "demo.Base"
-    for filename, key in (("parent.rs", "demo.Base"), ("child.rs", 
"demo.Child")):
-        directive = f"{C.RUST_SYNTAX.directive(policy)} {target}\n" if key == 
"demo.Base" else ""
-        (tmp_path / filename).write_text(
-            f"{directive}{C.RUST_SYNTAX.begin} 
object/{key}\n{C.RUST_SYNTAX.end}\n",
-            encoding="utf-8",
-        )
-    # Process the child first: generation must not depend on its parent's file 
order.
-    monkeypatch.setattr(
-        "sys.argv",
-        [
-            "tvm-ffi-stubgen",
-            "--target",
-            "rust",
-            str(tmp_path / "child.rs"),
-            str(tmp_path / "parent.rs"),
-        ],
-    )
-    assert stub_cli.__main__() == 0
-    for filename in ("parent.rs", "child.rs"):
-        text = (tmp_path / filename).read_text(encoding="utf-8")
-        if policy == "nullable":
-            assert "pub fn new(value: Option<ObjectRef>) -> Self" in text
-        else:
-            assert "/// Opaque:" in text
-            assert "fn new(" not in text
-    assert stub_cli.__main__() == 0
-
-
[email protected](shutil.which("cargo") is None, reason="Rust toolchain not 
installed")
-def test_generated_object_contracts_compile_and_drop(tmp_path: Path) -> None:
-    """Compile the generated code, including negative API checks, against the 
real crate."""
-    imports = RustImports()
-    RUST.add_directive(imports, "no-alloc", "testing.TestCxxClassBase", 1)
-    RUST.add_directive(imports, "nullable", "testing.TestDeepCopyEdges.v_obj", 
2)
-    RUST.add_directive(imports, "custom-new", "testing.TestDeepCopyEdges", 3)
-    keys = ["TestCxxClassBase", "TestCxxClassHiddenField", "TestObjectBase", 
"TestDeepCopyEdges"]
-    bodies = [_render(object_info_from_type_key(f"testing.{key}"), imports)[0] 
for key in keys]
-    docs = "\n".join(
-        f"//! ```compile_fail\n//! {snippet}\n//! ```"
-        for snippet in (
-            "use generated_contracts::TestCxxClassBase; let _ = 
TestCxxClassBase::new(1, 2);",
-            "fn send<T: Send>() {} 
send::<generated_contracts::TestCxxClassHiddenField>();",
-            "fn sync<T: Sync>() {} 
sync::<generated_contracts::TestCxxClassHiddenField>();",
-            "fn send<T: Send>() {} 
send::<generated_contracts::TestObjectBase>();",
-            "use generated_contracts::TestDeepCopyEdges; let _ = 
TestDeepCopyEdges::new(1.into(), None);",
-        )
-    )
-    source = docs + "\n" + "\n".join(item.as_use_line() for item in 
imports.items)
-    source += "\n" + "\n".join(bodies)
-    source += r"""
-// Keep semantic construction in the binding, using the existing nullable 
allocator.
-impl TestDeepCopyEdges {
-    pub fn new(v_any: tvm_ffi::Any, v_obj: tvm_ffi::object::ObjectRef) -> Self 
{
-        Self::from_complete_fields(v_any, Some(v_obj))
-    }
-}
-
-#[test]
-fn native_null_storage_is_safe_to_drop() {
-    use tvm_ffi::object::ObjectRefCore;
-    use tvm_ffi::tvm_ffi_sys::{TVMFFIAny, TVMFFIGetTypeInfo, 
TVMFFIFieldSetter, TVMFFITestingDummyTarget};
-    use 
tvm_ffi::tvm_ffi_sys::TVMFFIFieldFlagBitMask::kTVMFFIFieldFlagBitSetterIsFunctionObj;
-    assert_eq!(unsafe { TVMFFITestingDummyTarget() }, 0);
-    let child = TestObjectBase::new(1, 2.0, "child".into());
-    let object = 
tvm_ffi::object::ObjectRef::try_from(tvm_ffi::Any::from(child.clone())).unwrap();
-    let holder = TestDeepCopyEdges::new(7i64.into(), object);
-    assert!(holder.v_obj.as_ref().unwrap().same_as(&child));
-    let native_child = FieldGetter::new(TestDeepCopyEdgesObj::type_index(), 
"v_obj")
-        .unwrap().get_any(&*holder).unwrap();
-    
assert!(tvm_ffi::object::ObjectRef::try_from(native_child).unwrap().same_as(&child));
-    // Clear the field using the existing C++ reflection setter, leaving the 
same
-    // null storage as a native move-out. No borrowed field reference is live 
here.
-    unsafe {
-        let info = &*TVMFFIGetTypeInfo(TestDeepCopyEdgesObj::type_index());
-        let field = (0..info.num_fields as usize).map(|i| &*info.fields.add(i))
-            .find(|f| f.name.as_str() == "v_obj").unwrap();
-        assert!(!field.setter.is_null());
-        assert_eq!(field.flags & (kTVMFFIFieldFlagBitSetterIsFunctionObj as 
i64), 0);
-        let setter: TVMFFIFieldSetter = std::mem::transmute(field.setter);
-        let ptr = ObjectArc::as_raw(TestDeepCopyEdges::data(&holder));
-        assert_eq!(setter(ptr.cast_mut().cast::<u8>().offset(field.offset as 
isize).cast(),
-                         &TVMFFIAny::new()), 0);
-    }
-    assert!(holder.v_obj.is_none());
-    drop(holder);
-    assert_eq!(ObjectArc::strong_count(TestObjectBase::data(&child)), 1);
-}
-"""
-    (tmp_path / "src").mkdir()
-    (tmp_path / "src/lib.rs").write_text(source, encoding="utf-8")
-    crate = Path(__file__).resolve().parents[2] / "rust/tvm-ffi"
-    (tmp_path / "Cargo.toml").write_text(
-        '[package]\nname = "generated_contracts"\nversion = "0.0.0"\nedition = 
"2021"\n'
-        f'[dependencies]\ntvm-ffi = {{ path = "{crate.as_posix()}" }}\n',
-        encoding="utf-8",
-    )
-    env = os.environ.copy()
-    # Do not inherit a workspace target directory: parallel test runs are 
independent.
-    env["CARGO_TARGET_DIR"] = str(tmp_path / "target")
-    loader = (
-        "PATH"
-        if sys.platform == "win32"
-        else ("DYLD_LIBRARY_PATH" if sys.platform == "darwin" else 
"LD_LIBRARY_PATH")
-    )
-    env[loader] = str(Path(find_libtvm_ffi()).parent) + os.pathsep + 
env.get(loader, "")
-    result = subprocess.run(
-        ["cargo", "test", "--quiet"],
-        cwd=tmp_path,
-        env=env,
-        capture_output=True,
-        text=True,
-        check=False,
-        timeout=180,
-    )
-    assert result.returncode == 0, result.stdout + result.stderr
-
-
-def test_cli_rejects_unknown_no_alloc_type(tmp_path: Path, monkeypatch: 
pytest.MonkeyPatch) -> None:
-    src = tmp_path / "mod.rs"
-    original = (
-        f"{C.RUST_SYNTAX.directive('no-alloc')} 
testing.MissingObjectContract\n"
-        f"{C.RUST_SYNTAX.begin} 
object/testing.TestCxxClassBase\n{C.RUST_SYNTAX.end}\n"
-    )
-    src.write_text(original, encoding="utf-8")
-    monkeypatch.setattr("sys.argv", ["tvm-ffi-stubgen", "--target", "rust", 
str(tmp_path)])
-    assert stub_cli.__main__() == 2
-    assert src.read_text(encoding="utf-8") == original
-
-
 def test_cli_init_generates_a_module_tree(tmp_path: Path, monkeypatch: 
pytest.MonkeyPatch) -> None:
     monkeypatch.setattr(
         "sys.argv",

Reply via email to