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 4210572d [FEAT] Add consuming casts and raw adoption to UnchangedOr
(#791)
4210572d is described below
commit 4210572d0e83f8bfb160a19457ef3369c3fe0bae
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Sep 14 20:19:11 2026 -0400
[FEAT] Add consuming casts and raw adoption to UnchangedOr (#791)
Add rvalue-qualified `UnchangedOr<T>::as<U>()` and `as_or_throw<U>()`
for strict casts to an exact target type. Forwarding to `Any` preserves
its supported types, mismatch behavior and ownership transfer, including
successful unchanged results when the target is another `UnchangedOr`.
Add the symmetric raw-storage adoption helper for ABI callers that
already guarantee an unchanged marker or a valid replacement.
---
include/tvm/ffi/extra/structural_mutate.h | 39 +++++++++++++++++++++++++++++++
tests/cpp/extra/test_structural_mutate.cc | 10 ++++++++
2 files changed, 49 insertions(+)
diff --git a/include/tvm/ffi/extra/structural_mutate.h
b/include/tvm/ffi/extra/structural_mutate.h
index 34ee6c03..c8bc0178 100644
--- a/include/tvm/ffi/extra/structural_mutate.h
+++ b/include/tvm/ffi/extra/structural_mutate.h
@@ -353,6 +353,31 @@ class UnchangedOr {
return details::AnyUnsafe::MoveFromAnyAfterCheck<T>(std::move(data_));
}
+ /*!
+ * \brief Strictly cast the stored result, moving it on success.
+ * \tparam U The exact target type, including any UnchangedOr wrapper.
+ * \return The cast value, or std::nullopt on a type mismatch.
+ * \note Unchanged succeeds for an UnchangedOr target. No fallback
conversions are run.
+ */
+ template <typename U,
+ typename = std::enable_if_t<TypeTraits<U>::storage_enabled ||
std::is_same_v<U, Any>>>
+ TVM_FFI_INLINE std::optional<U> as() && {
+ return std::move(data_).template as<U>();
+ }
+
+ /*!
+ * \brief Strictly cast the stored result, moving it on success, or throw.
+ * \tparam U The exact target type, including any UnchangedOr wrapper.
+ * \return The cast value.
+ * \throws Error on a type mismatch.
+ * \note Unchanged succeeds for an UnchangedOr target. No fallback
conversions are run.
+ */
+ template <typename U,
+ typename = std::enable_if_t<TypeTraits<U>::storage_enabled ||
std::is_same_v<U, Any>>>
+ TVM_FFI_INLINE U as_or_throw() && {
+ return std::move(data_).template as_or_throw<U>();
+ }
+
private:
template <typename>
friend class UnchangedOr;
@@ -367,6 +392,20 @@ class UnchangedOr {
namespace details {
/*! \brief Unsafe moves between UnchangedOr and its single Any storage. */
struct UnchangedOrUnsafe {
+ /*!
+ * \brief Adopt an owning raw FFI value into UnchangedOr storage.
+ * \tparam T The replacement type.
+ * \param raw The raw FFI value whose ownership is transferred to the result.
+ * \return UnchangedOr backed by the adopted Any storage.
+ * \pre The caller guarantees that raw holds Unchanged or a valid T
replacement.
+ * \note No type check, reference increment, or value conversion is
performed.
+ */
+ template <typename T>
+ TVM_FFI_INLINE static UnchangedOr<T> MoveFromTVMFFIAny(TVMFFIAny raw) {
+ return UnchangedOr<T>(typename UnchangedOr<T>::UnsafeInit{},
+ AnyUnsafe::MoveTVMFFIAnyRawToAny(raw));
+ }
+
template <typename T>
TVM_FFI_INLINE static TVMFFIAny MoveToTVMFFIAny(UnchangedOr<T>&& result)
noexcept {
return AnyUnsafe::MoveAnyToTVMFFIAny(std::move(result.data_));
diff --git a/tests/cpp/extra/test_structural_mutate.cc
b/tests/cpp/extra/test_structural_mutate.cc
index f60bdf3e..4222eac0 100644
--- a/tests/cpp/extra/test_structural_mutate.cc
+++ b/tests/cpp/extra/test_structural_mutate.cc
@@ -62,6 +62,16 @@ TEST(UnchangedOr, BareValueForwarding) {
EXPECT_DOUBLE_EQ(std::move(numeric).ValueUnchecked(), 42.0);
}
+TEST(UnchangedOr, PairedCasts) {
+ UnchangedOr<Any> replacement = TInt(42);
+ if (auto matched = std::move(replacement).as<TInt>()) {
+ EXPECT_EQ((*matched)->value, 42);
+ } else {
+ FAIL() << "Expected TInt replacement";
+ }
+
EXPECT_TRUE(UnchangedOr<Any>(Unchanged()).as_or_throw<UnchangedOr<TInt>>().IsUnchanged());
+}
+
TEST(UnchangedOr, ConversionsAndAssignmentMacro) {
static_assert(!std::is_convertible_v<UnchangedOr<Any>, UnchangedOr<int>>);
static_assert(type_subsumes_v<Expected<UnchangedOr<TNumber>>,
Expected<UnchangedOr<TInt>>>);