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 a54d3f67 [FIX] Forward implicitly convertible values into UnchangedOr 
(#782)
a54d3f67 is described below

commit a54d3f67fb1f9dd640c0c6f34bb4e513a9637459
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun Sep 13 11:54:36 2026 -0400

    [FIX] Forward implicitly convertible values into UnchangedOr (#782)
    
    Allow values implicitly convertible to the replacement type to
    initialize UnchangedOr directly, enabling bare object returns through
    Expected<UnchangedOr<Any>>. The constrained C++17 forwarding constructor
    materializes the replacement type and preserves dedicated unchanged,
    error, and wrapper conversions.
---
 include/tvm/ffi/extra/structural_mutate.h | 22 ++++++++++++++++++++++
 tests/cpp/extra/test_structural_mutate.cc |  9 +++++++++
 2 files changed, 31 insertions(+)

diff --git a/include/tvm/ffi/extra/structural_mutate.h 
b/include/tvm/ffi/extra/structural_mutate.h
index 358866c2..601d1bc6 100644
--- a/include/tvm/ffi/extra/structural_mutate.h
+++ b/include/tvm/ffi/extra/structural_mutate.h
@@ -156,6 +156,12 @@ template <typename Parent>
 class StructuralMutateDynEngine;
 
 struct UnchangedOrUnsafe;
+
+template <typename T>
+inline constexpr bool is_unchanged_or_v = false;
+
+template <typename T>
+inline constexpr bool is_unchanged_or_v<UnchangedOr<T>> = true;
 }  // namespace details
 
 /*! \brief Tag for a mutation result that produced no new value. */
@@ -220,6 +226,22 @@ class UnchangedOr {
   // NOLINTNEXTLINE(google-explicit-constructor,runtime/explicit)
   TVM_FFI_INLINE UnchangedOr(T value) : data_(Any(std::move(value))) {}
 
+  /*!
+   * \brief Construct a changed result from an implicitly convertible 
replacement value.
+   * \tparam U Source value type, implicitly convertible to T.
+   * \param value The replacement value to copy or move.
+   */
+  // Preserve the dedicated tag and wrapper routes. Subsumption applies only 
to materialized
+  // wrapper storage; a bare value must first be implicitly convertible to T.
+  template <typename U, typename = 
std::enable_if_t<!std::is_same_v<std::decay_t<U>, Unchanged> &&
+                                                    
!details::is_unchanged_or_v<std::decay_t<U>> &&
+                                                    
!details::is_expected_v<std::decay_t<U>> &&
+                                                    
!details::is_unexpected_v<std::decay_t<U>> &&
+                                                    !std::is_base_of_v<Error, 
std::decay_t<U>> &&
+                                                    std::is_convertible_v<U, 
T>>>
+  // NOLINTNEXTLINE(google-explicit-constructor,runtime/explicit)
+  TVM_FFI_INLINE UnchangedOr(U&& value) : 
data_(Any(T(std::forward<U>(value)))) {}
+
   /*!
    * \brief Implicit converting constructor from another replacement type.
    * \tparam U Source replacement type whose storage is subsumed by or 
implicitly convertible to T.
diff --git a/tests/cpp/extra/test_structural_mutate.cc 
b/tests/cpp/extra/test_structural_mutate.cc
index ad9724d4..2866d1ca 100644
--- a/tests/cpp/extra/test_structural_mutate.cc
+++ b/tests/cpp/extra/test_structural_mutate.cc
@@ -54,6 +54,15 @@ static_assert(
 
 Expected<UnchangedOr<String>> ReturnTypedUnchangedExpected() noexcept { return 
Unchanged(); }
 
+TEST(UnchangedOr, BareValueForwarding) {
+  TInt value(42);
+  auto convert = [](TInt& value) -> Expected<UnchangedOr<Any>> { return value; 
};
+  
EXPECT_TRUE(std::move(convert(value)).value().ValueUnchecked().same_as(value));
+  UnchangedOr<double> numeric = 42;
+  EXPECT_EQ(AnyView(numeric).type_index(), TypeIndex::kTVMFFIFloat);
+  EXPECT_DOUBLE_EQ(std::move(numeric).ValueUnchecked(), 42.0);
+}
+
 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