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 4ee5a862 [FIX] Preserve NativeFunction errors and reject null payloads 
(#823)
4ee5a862 is described below

commit 4ee5a8621a075150ba1f2356893397d9d08d073e
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Sep 28 07:25:24 2026 -0400

    [FIX] Preserve NativeFunction errors and reject null payloads (#823)
---
 include/tvm/ffi/reflection/native_function.h           | 10 ++++++++--
 ...native_function_view.cc => test_native_function.cc} | 18 ++++++++++++++++++
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/include/tvm/ffi/reflection/native_function.h 
b/include/tvm/ffi/reflection/native_function.h
index d42562f0..6f98438f 100644
--- a/include/tvm/ffi/reflection/native_function.h
+++ b/include/tvm/ffi/reflection/native_function.h
@@ -96,9 +96,11 @@ class NativeFunctionView<Expected<R>(Args...)> {
    */
   template <Expected<R> (*Fn)(Args...) noexcept>
   static NativeFunctionView FromNative() {
+    static_assert(Fn != nullptr, "NativeFunctionView requires a non-null 
native function");
     TVMFFIAny data;
     data.type_index = TypeIndex::kTVMFFIOpaquePtr;
     data.zero_padding = 0;
+    TVM_FFI_CLEAR_PTR_PADDING_IN_FFI_ANY(&data);
     data.v_ptr = reinterpret_cast<void*>(&NativeABIFuncPtr<Fn>);
     return NativeFunctionView(UnsafeInit{}, data);
   }
@@ -141,6 +143,10 @@ class NativeFunctionView<Expected<R>(Args...)> {
       return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(
           Expected<R>(Unexpected(ffi::details::MoveFromSafeCallRaised())));
     }
+    if (result.type_index() == TypeIndex::kTVMFFIError) {
+      return ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(Expected<R>(
+          
Unexpected(ffi::details::AnyUnsafe::MoveFromAnyAfterCheck<Error>(std::move(result)))));
+    }
     if constexpr (std::is_same_v<R, Any>) {
       return 
ffi::details::ExpectedUnsafe::MoveToTVMFFIAny(Expected<R>(std::move(result)));
     } else if constexpr (std::is_void_v<R>) {
@@ -258,8 +264,8 @@ struct 
TypeTraits<reflection::NativeFunctionView<Signature>> : public TypeTraits
     *result = 
details::AnyUnsafe::MoveAnyToTVMFFIAny(Any(AnyView::CopyFromTVMFFIAny(src.data_)));
   }
   static bool CheckAnyStrict(const TVMFFIAny* src) {
-    return (src->type_index == TypeIndex::kTVMFFIOpaquePtr ||
-            src->type_index == TypeIndex::kTVMFFIFunction);
+    return (src->type_index == TypeIndex::kTVMFFIOpaquePtr && src->v_ptr != 
nullptr) ||
+           src->type_index == TypeIndex::kTVMFFIFunction;
   }
   static View CopyFromAnyViewAfterCheck(const TVMFFIAny* src) { return 
View(UnsafeInit{}, *src); }
   static View MoveFromAnyAfterCheck(TVMFFIAny* src) { return 
CopyFromAnyViewAfterCheck(src); }
diff --git a/tests/cpp/test_native_function_view.cc 
b/tests/cpp/test_native_function.cc
similarity index 82%
rename from tests/cpp/test_native_function_view.cc
rename to tests/cpp/test_native_function.cc
index 09eb195c..08a4cd1e 100644
--- a/tests/cpp/test_native_function_view.cc
+++ b/tests/cpp/test_native_function.cc
@@ -69,10 +69,28 @@ TEST(NativeFunctionView, ErrorAndNullRejection) {
   ASSERT_TRUE(failed.is_err());
   EXPECT_EQ(failed.error().kind(), "ValueError");
 
+  Error packed_error("ValueError", "packed failure", "");
+  TypedFunction<Expected<int>(int)> packed(Function::FromTyped(
+      [packed_error](int) -> Expected<int> { return Unexpected(packed_error); 
}));
+  auto packed_failed = NativeFunctionView<Expected<int>(int)>(packed)(0);
+  ASSERT_TRUE(packed_failed.is_err());
+  EXPECT_TRUE(packed_failed.error().same_as(packed_error));
+
+  TypedFunction<Expected<void>()> packed_void(Function::FromTyped(
+      []() -> Expected<void> { return Unexpected(Error("ValueError", "void 
failure", "")); }));
+  auto void_failed = NativeFunctionView<Expected<void>()>(packed_void)();
+  ASSERT_TRUE(void_failed.is_err());
+  EXPECT_EQ(void_failed.error().message(), "void failure");
+
   TypedFunction<Expected<int>(int)> empty(nullptr);
   EXPECT_ANY_THROW(NativeFunctionView<Expected<int>(int)> invalid(empty));
   
EXPECT_FALSE(Any(nullptr).try_cast<NativeFunctionView<Expected<int>(int)>>().has_value());
   
EXPECT_FALSE(Any(nullptr).try_cast<NativeFunction<Expected<int>(int)>>().has_value());
+  EXPECT_FALSE(Any(static_cast<void*>(nullptr))
+                   .try_cast<NativeFunctionView<Expected<int>(int)>>()
+                   .has_value());
+  EXPECT_FALSE(
+      
Any(static_cast<void*>(nullptr)).try_cast<NativeFunction<Expected<int>(int)>>().has_value());
 }
 
 TEST(NativeFunctionView, OwningLifetimeAndAnyRoundtrip) {

Reply via email to