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 bdad218  fix: Type mismatch aborts on OpaquePyObject (#56)
bdad218 is described below

commit bdad2184551353e49a9b6882f7b9a75a258862bb
Author: Junru Shao <[email protected]>
AuthorDate: Thu Sep 25 12:38:37 2025 -0700

    fix: Type mismatch aborts on OpaquePyObject (#56)
    
    In
    [#49](https://github.com/apache/tvm-ffi/pull/49#issuecomment-3331042097),
    We noticed an issue with `OpaquePyObject`:
    
    ```
    Exception caught during TVMFFIGetTypeInfo:
    Traceback (most recent call last):
      File "include/tvm/ffi/type_traits.h", line 92, in 
tvm::ffi::TypeTraitsBase::GetMismatchTypeInfo[abi:cxx11](TVMFFIAny const*)
      File "include/tvm/ffi/object.h", line 121, in 
tvm::ffi::TypeIndexToTypeKey[abi:cxx11](int)
      File "src/ffi/object.cc", line 493, in TVMFFIGetTypeInfo
      File "src/ffi/object.cc", line 193, in tvm::ffi::TypeTable::Entry* 
tvm::ffi::TypeTable::GetTypeEntry(int32_t)
    InternalError: Check failed: (entry != nullptr) is false: Cannot find type 
info for type_index=74
    ```
    
    where when an `OpaquePyObject` is fed into a TVM-FFI function, which
    expects a different type (e.g. `int`), the entire program aborts without
    proper error message inside `TVMFFIGetTypeInfo`, which uses `exit(1)`
    instead of C error code.
    
    This PR fixes this issue by registering
    `TypeIndex::kTVMFFIOpaquePyObject` as a builtin type index.
---
 include/tvm/ffi/object.h    |  2 ++
 python/tvm_ffi/testing.py   |  5 +++++
 src/ffi/extra/testing.cc    |  1 +
 src/ffi/object.cc           |  2 +-
 tests/python/test_object.py | 10 ++++++++++
 5 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/include/tvm/ffi/object.h b/include/tvm/ffi/object.h
index 93ef6d8..64db8d7 100644
--- a/include/tvm/ffi/object.h
+++ b/include/tvm/ffi/object.h
@@ -110,6 +110,8 @@ struct StaticTypeKey {
   static constexpr const char* kTVMFFIMap = "ffi.Map";
   /*! \brief The type key for Module */
   static constexpr const char* kTVMFFIModule = "ffi.Module";
+  /*! \brief The type key for OpaquePyObject */
+  static constexpr const char* kTVMFFIOpaquePyObject = "ffi.OpaquePyObject";
 };
 
 /*!
diff --git a/python/tvm_ffi/testing.py b/python/tvm_ffi/testing.py
index 155030b..dbf8636 100644
--- a/python/tvm_ffi/testing.py
+++ b/python/tvm_ffi/testing.py
@@ -89,6 +89,11 @@ def make_unregistered_object() -> Object:
     return get_global_func("testing.make_unregistered_object")()
 
 
+def add_one(x: int) -> int:
+    """Add one to the input integer."""
+    return get_global_func("testing.add_one")(x)
+
+
 @c_class("testing.TestCxxClassBase")
 class _TestCxxClassBase:
     v_i64: int
diff --git a/src/ffi/extra/testing.cc b/src/ffi/extra/testing.cc
index f67752f..5d7ef58 100644
--- a/src/ffi/extra/testing.cc
+++ b/src/ffi/extra/testing.cc
@@ -217,6 +217,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
 
   refl::GlobalDef()
       .def("testing.test_raise_error", TestRaiseError)
+      .def("testing.add_one", [](int x) { return x + 1; })
       .def_packed("testing.nop", [](PackedArgs args, Any* ret) {})
       .def_packed("testing.echo", [](PackedArgs args, Any* ret) { *ret = 
args[0]; })
       .def_packed("testing.apply", TestApply)
diff --git a/src/ffi/object.cc b/src/ffi/object.cc
index d9c6698..aa7edbb 100644
--- a/src/ffi/object.cc
+++ b/src/ffi/object.cc
@@ -29,7 +29,6 @@
 #include <tvm/ffi/string.h>
 
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -340,6 +339,7 @@ class TypeTable {
                             TypeIndex::kTVMFFIObjectRValueRef);
     ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFISmallStr, 
TypeIndex::kTVMFFISmallStr);
     ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFISmallBytes, 
TypeIndex::kTVMFFISmallBytes);
+    ReserveBuiltinTypeIndex(StaticTypeKey::kTVMFFIOpaquePyObject, 
TypeIndex::kTVMFFIOpaquePyObject);
     // no need to reserve for object types as they will be registered
   }
 
diff --git a/tests/python/test_object.py b/tests/python/test_object.py
index 0c64e46..d23aa10 100644
--- a/tests/python/test_object.py
+++ b/tests/python/test_object.py
@@ -103,6 +103,16 @@ def test_opaque_object() -> None:
     assert sys.getrefcount(obj0) == 2
 
 
+def test_opaque_type_error() -> None:
+    obj0 = MyObject("hello")
+    with pytest.raises(TypeError) as e:
+        tvm_ffi.testing.add_one(obj0)  # type: ignore[arg-type]
+    assert (
+        "Mismatched type on argument #0 when calling: `testing.add_one(0: int) 
-> int`. Expected `int` but got `ffi.OpaquePyObject`"
+        in str(e.value)
+    )
+
+
 def test_unregistered_object_fallback() -> None:
     def _check_type(x: Any) -> None:
         type_info: TypeInfo = type(x).__tvm_ffi_type_info__  # type: 
ignore[attr-defined]

Reply via email to