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());