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

Reply via email to