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 df463f9b [FIX] Honor specialized strict checks in ObjectRef casts 
(#836)
df463f9b is described below

commit df463f9bd269867fc7f4d578d62d80cc2a11d004
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Oct 2 10:45:51 2026 -0400

    [FIX] Honor specialized strict checks in ObjectRef casts (#836)
    
    Dispatch inherited ObjectRef casts through the final TypeTraits
    specialization’s strict check before copying storage, so custom semantic
    checks also apply to conversions.
    
    Document how a specialization can customize only CheckAnyStrict while
    inheriting cast, copy, and move behavior.
---
 include/tvm/ffi/object.h | 28 +++++++++++++++++-----------
 tests/cpp/test_object.cc | 22 ++++++++++++++++++++++
 2 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/include/tvm/ffi/object.h b/include/tvm/ffi/object.h
index 81ac2b10..f8269c62 100644
--- a/include/tvm/ffi/object.h
+++ b/include/tvm/ffi/object.h
@@ -1577,6 +1577,21 @@ struct TypeTraits<Arc<TObject>,
   }
 };
 
+/*!
+ * \brief Default type traits for an ObjectRef subclass.
+ * \tparam TObjRef The ObjectRef subclass.
+ *
+ * A TypeTraits<TObjRef> specialization deriving from this base can customize 
only
+ * CheckAnyStrict while inheriting the default cast, copy, and move operations.
+ * TryCastFromAnyView statically dispatches to 
TypeTraits<TObjRef>::CheckAnyStrict,
+ * so the final specialization's checks apply without virtual dispatch. On 
success,
+ * it reuses CopyFromAnyViewAfterCheck.
+ *
+ * A custom CheckAnyStrict must validate the storage and nullability 
requirements
+ * of TObjRef before accepting a value. It can call this base's CheckAnyStrict 
to
+ * check those requirements before applying additional semantic constraints. It
+ * must not call the inherited TryCastFromAnyView, which would recurse.
+ */
 template <typename TObjRef>
 struct ObjectRefTypeTraitsBase : public TypeTraitsBase {
   static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIObject;
@@ -1643,17 +1658,8 @@ struct ObjectRefTypeTraitsBase : public TypeTraitsBase {
   }
 
   TVM_FFI_INLINE static std::optional<TObjRef> TryCastFromAnyView(const 
TVMFFIAny* src) {
-    if constexpr (TObjRef::_type_is_nullable) {
-      if (src->type_index == TypeIndex::kTVMFFINone) {
-        return details::ObjectUnsafe::ObjectRefFromObjectPtr<TObjRef>(nullptr);
-      }
-    }
-    if (src->type_index >= TypeIndex::kTVMFFIStaticObjectBegin &&
-        details::IsObjectInstance<ContainerType>(src->type_index)) {
-      return details::ObjectUnsafe::ObjectRefFromObjectPtr<TObjRef>(
-          
details::ObjectUnsafe::ObjectPtrFromUnowned<ContainerType>(src->v_obj));
-    }
-    return std::nullopt;
+    if (!TypeTraits<TObjRef>::CheckAnyStrict(src)) return std::nullopt;
+    return CopyFromAnyViewAfterCheck(src);
   }
 
   TVM_FFI_INLINE static std::string TypeStr() { return 
ContainerType::_type_key; }
diff --git a/tests/cpp/test_object.cc b/tests/cpp/test_object.cc
index a34efd98..23c7cd38 100644
--- a/tests/cpp/test_object.cc
+++ b/tests/cpp/test_object.cc
@@ -48,6 +48,12 @@ class TIntOrFloatRef : public ObjectRef {
   using ContainerType = Object;
 };
 
+class TPositiveIntRef : public ObjectRef {
+ public:
+  static constexpr bool _type_container_is_exact = false;
+  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TPositiveIntRef, ObjectRef, 
TIntObj);
+};
+
 }  // namespace testing
 
 template <>
@@ -59,6 +65,15 @@ struct TypeTraits<testing::TIntOrFloatRef>
   }
 };
 
+template <>
+struct TypeTraits<testing::TPositiveIntRef>
+    : public ObjectRefTypeTraitsBase<testing::TPositiveIntRef> {
+  TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
+    return 
ObjectRefTypeTraitsBase<testing::TPositiveIntRef>::CheckAnyStrict(src) &&
+           
details::ObjectUnsafe::RawObjectPtrFromUnowned<testing::TIntObj>(src->v_obj)->value
 > 0;
+  }
+};
+
 template <typename ObjectType>
 inline constexpr bool object_ref_contains_v<testing::TIntOrFloatRef, 
ObjectType> =
     std::is_base_of_v<testing::TIntObj, ObjectType> ||
@@ -249,6 +264,13 @@ TEST(ObjectRef, AsUsesTypeTraitsCheckAnyStrict) {
   EXPECT_NE((*float_like).as<TFloatObj>(), nullptr);  // 
NOLINT(bugprone-unchecked-optional-access)
 }
 
+TEST(ObjectRef, StrictOnlyTraits) {
+  Any invalid = TInt(-1);
+  Any valid = TInt(1);
+  EXPECT_FALSE(invalid.try_cast<TPositiveIntRef>().has_value());
+  EXPECT_EQ(valid.cast<TPositiveIntRef>()->value, 1);
+}
+
 TEST(ObjectRef, GetRefUsesObjectRefContainment) {
   ObjectPtr<TIntObj> int_object = make_object<TIntObj>(10);
   TIntOrFloatRef int_or_float = GetRef<TIntOrFloatRef>(int_object.get());

Reply via email to