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) {