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 daf594da fix: preserve lvalues in UnchangedOr value access (#793)
daf594da is described below
commit daf594da8e6950fdc02ae35fcf4e0ab2e59a4979
Author: Tianqi Chen <[email protected]>
AuthorDate: Tue Sep 15 07:33:32 2026 -0400
fix: preserve lvalues in UnchangedOr value access (#793)
Remove the mutable-lvalue ValueOrUnchanged overload so lvalues use the
borrowing const-reference overload; ownership transfer requires an
explicit rvalue. Clarify the accessor documentation and add a focused
lvalue-preservation regression test. Existing callers already express
intentional transfers explicitly.
---
include/tvm/ffi/extra/structural_mutate.h | 14 +++-----------
tests/cpp/extra/test_structural_mutate.cc | 7 +++++++
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/include/tvm/ffi/extra/structural_mutate.h
b/include/tvm/ffi/extra/structural_mutate.h
index c8bc0178..d3e59514 100644
--- a/include/tvm/ffi/extra/structural_mutate.h
+++ b/include/tvm/ffi/extra/structural_mutate.h
@@ -300,21 +300,12 @@ class UnchangedOr {
return IsUnchanged() || data_.same_as(original);
}
- /*!
- * \brief Move the replacement, or move \p original when unchanged.
- * \param original The owned original value.
- * \return The replacement or original value.
- * \note Passing a named lvalue transfers ownership and may leave it
moved-from.
- */
- TVM_FFI_INLINE T ValueOrUnchanged(T& original) && {
- return IsUnchanged() ? std::move(original)
- :
details::AnyUnsafe::MoveFromAnyAfterCheck<T>(std::move(data_));
- }
-
/*!
* \brief Move the replacement, or copy \p original when unchanged.
* \param original The borrowed original value, which is left unmodified.
* \return The replacement or original value.
+ * \note Both mutable and const lvalues are borrowed. Use
std::move(original) to transfer
+ * ownership.
*/
TVM_FFI_INLINE T ValueOrUnchanged(const T& original) && {
return IsUnchanged() ? original
@@ -325,6 +316,7 @@ class UnchangedOr {
* \brief Move the replacement, or move \p original when unchanged.
* \param original The owned original value.
* \return The replacement or original value.
+ * \note The original is moved from only when the result is unchanged.
*/
TVM_FFI_INLINE T ValueOrUnchanged(T&& original) && {
return IsUnchanged() ? std::move(original)
diff --git a/tests/cpp/extra/test_structural_mutate.cc
b/tests/cpp/extra/test_structural_mutate.cc
index 4222eac0..1debe194 100644
--- a/tests/cpp/extra/test_structural_mutate.cc
+++ b/tests/cpp/extra/test_structural_mutate.cc
@@ -72,6 +72,13 @@ TEST(UnchangedOr, PairedCasts) {
EXPECT_TRUE(UnchangedOr<Any>(Unchanged()).as_or_throw<UnchangedOr<TInt>>().IsUnchanged());
}
+TEST(UnchangedOr, ValueOrUnchangedBorrowsLvalues) {
+ TInt original(7);
+ TInt result = UnchangedOr<TInt>(Unchanged()).ValueOrUnchanged(original);
+ EXPECT_TRUE(result.same_as(original));
+ EXPECT_EQ(original.use_count(), 2);
+}
+
TEST(UnchangedOr, ConversionsAndAssignmentMacro) {
static_assert(!std::is_convertible_v<UnchangedOr<Any>, UnchangedOr<int>>);
static_assert(type_subsumes_v<Expected<UnchangedOr<TNumber>>,
Expected<UnchangedOr<TInt>>>);