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.git


The following commit(s) were added to refs/heads/main by this push:
     new 87b5b27dac [CLEANUP][IR] Simplify exception-first traversal (#20347)
87b5b27dac is described below

commit 87b5b27dacfc8ffa003db5cbd9cd123568b3a2ca
Author: Tianqi Chen <[email protected]>
AuthorDate: Tue Sep 15 14:25:59 2026 -0400

    [CLEANUP][IR] Simplify exception-first traversal (#20347)
    
    Simplify plain analyzer ownership and traversal result handling while
    preserving explicit ownership-transfer boundaries. Remove redundant
    wrappers and allocations without changing traversal behavior.
---
 include/tvm/arith/analyzer.h         |   6 +-
 include/tvm/ir/object_functor.h      |  14 +-
 src/arith/canonical_simplify.cc      |   6 +-
 src/arith/const_int_bound.cc         |   5 +-
 src/arith/iter_affine_map.cc         |  16 +-
 src/arith/modular_set.cc             |   5 +-
 src/arith/rewrite_simplify.cc        |   9 +-
 src/arith/simplify_base.cc           |  67 +++----
 src/arith/simplify_base.h            |   2 -
 src/arith/solve_linear_inequality.cc |   7 +-
 src/arith/z3_prover.cc               |   6 +-
 src/ir/expr.cc                       | 202 ++++++++++-----------
 src/ir/expr_functor.cc               | 340 +++++++++++++++++------------------
 src/ir/prim/expr.cc                  | 121 +++++++------
 src/ir/prim/vector_expr.cc           |  80 +++++----
 src/ir/type.cc                       |  52 +++---
 16 files changed, 458 insertions(+), 480 deletions(-)

diff --git a/include/tvm/arith/analyzer.h b/include/tvm/arith/analyzer.h
index 889c02193c..08f85948ff 100644
--- a/include/tvm/arith/analyzer.h
+++ b/include/tvm/arith/analyzer.h
@@ -193,7 +193,7 @@ class ConstIntBoundAnalyzer {
   struct Entry;
   class Impl;
   /*! \brief Internal impl */
-  ffi::ObjectPtr<Impl> impl_;
+  std::unique_ptr<Impl> impl_;
 };
 
 /*!
@@ -273,7 +273,7 @@ class ModularSetAnalyzer {
   struct Entry;
   class Impl;
   /*! \brief Internal impl */
-  ffi::ObjectPtr<Impl> impl_;
+  std::unique_ptr<Impl> impl_;
 };
 
 /*!
@@ -715,7 +715,7 @@ class Z3Prover {
   TVM_DLL ~Z3Prover();
   void CopyFrom(const Z3Prover& other);
   class Impl;
-  ffi::ObjectPtr<Impl> impl_;
+  std::unique_ptr<Impl> impl_;
 };
 
 /*!
diff --git a/include/tvm/ir/object_functor.h b/include/tvm/ir/object_functor.h
index f3bfe2e7e0..33a0a33ae2 100644
--- a/include/tvm/ir/object_functor.h
+++ b/include/tvm/ir/object_functor.h
@@ -202,7 +202,7 @@ class ObjectFunctor<R(NodeArg, Args...)> {
   [[noreturn]] TVM_FFI_COLD_CODE static void ThrowUnregistered(NodeArg n) {
     TVM_FFI_THROW(InternalError) << "ObjectFunctor calls un-registered 
function on type "
                                  << n->GetTypeKey();
-    throw;
+    TVM_FFI_UNREACHABLE();
   }
 
   /*! \brief internal function pointer type */
@@ -229,8 +229,6 @@ class TVM_DLL ObjectVisitor : public 
ffi::StructuralVisitorObj {
  public:
   /*! \brief Construct a visitor using structural fallback for every value. */
   ObjectVisitor() : ObjectVisitor(GlobalVTable()) {}
-  /*! \brief Release the managed visitor state. */
-  ~ObjectVisitor() = default;
   ObjectVisitor(const ObjectVisitor& other) = delete;
   ObjectVisitor& operator=(const ObjectVisitor& other) = delete;
 
@@ -265,8 +263,8 @@ class TVM_DLL ObjectVisitor : public 
ffi::StructuralVisitorObj {
   Expected<ffi::Optional<VisitInterrupt>> VisitExpected(ffi::AnyView value) 
noexcept {
     try {
       return Visit(value);
-    } catch (const ffi::Error& error) {
-      return ffi::Unexpected(error);
+    } catch (ffi::Error& error) {
+      return ffi::Unexpected(std::move(error));
     } catch (const std::exception& error) {
       return ffi::Unexpected(ffi::Error("InternalError", error.what(), ""));
     }
@@ -349,8 +347,6 @@ class TVM_DLL ObjectMutator : public 
ffi::StructuralMapEngineBase {
  public:
   /*! \brief Construct a mutator using structural fallback for every object. */
   ObjectMutator() : ObjectMutator(GlobalVTable()) {}
-  /*! \brief Release the managed mutator state. */
-  ~ObjectMutator() = default;
   ObjectMutator(const ObjectMutator& other) = delete;
   ObjectMutator& operator=(const ObjectMutator& other) = delete;
 
@@ -410,8 +406,8 @@ class TVM_DLL ObjectMutator : public 
ffi::StructuralMapEngineBase {
       ffi::AnyView value, InplaceMode inplace_mode = InplaceMode::kDisallow) 
noexcept {
     try {
       return Mutate(value, inplace_mode);
-    } catch (const ffi::Error& error) {
-      return ffi::Unexpected(error);
+    } catch (ffi::Error& error) {
+      return ffi::Unexpected(std::move(error));
     } catch (const std::exception& error) {
       return ffi::Unexpected(ffi::Error("InternalError", error.what(), ""));
     }
diff --git a/src/arith/canonical_simplify.cc b/src/arith/canonical_simplify.cc
index 2f48809233..ba6d5a7a0d 100644
--- a/src/arith/canonical_simplify.cc
+++ b/src/arith/canonical_simplify.cc
@@ -589,13 +589,11 @@ class CanonicalSimplifier::Impl : public 
RewriteSimplifier::Impl {
       if (expr_u.IsUnchanged()) return ffi::Unchanged();
       ffi::Any expr = std::move(expr_u).ValueUnchecked();
       if (auto prim_expr = expr.as<PrimExpr>()) {
-        return Normalize(prim_expr.value());
+        return Normalize(*std::move(prim_expr));
       }
       return expr;
     } catch (ffi::Error& error) {
-      if (const auto* node = value.as<ffi::Object>()) {
-        ffi::details::UpdateVisitErrorContext(error, 
ffi::GetRef<ffi::ObjectRef>(node));
-      }
+      ffi::details::UpdateVisitErrorContext(error, value);
       throw;
     }
   }
diff --git a/src/arith/const_int_bound.cc b/src/arith/const_int_bound.cc
index 1e4670feae..78c6d63c37 100644
--- a/src/arith/const_int_bound.cc
+++ b/src/arith/const_int_bound.cc
@@ -94,8 +94,7 @@ struct ConstIntBoundAnalyzer::Entry {
 };
 
 class ConstIntBoundAnalyzer::Impl
-    : public ffi::Object,
-      public tvm::ExprFunctor<ConstIntBoundAnalyzer::Entry(const Expr&)> {
+    : public tvm::ExprFunctor<ConstIntBoundAnalyzer::Entry(const Expr&)> {
  public:
   explicit Impl(AnalyzerObj* parent) : parent_(parent) {}
   /*! \brief additional bound info about expr in bound */
@@ -910,7 +909,7 @@ std::function<void()> 
ConstIntBoundAnalyzer::EnterConstraint(const PrimExpr& con
 }
 
 ConstIntBoundAnalyzer::ConstIntBoundAnalyzer(AnalyzerObj* parent)
-    : impl_(ffi::make_object<Impl>(parent)) {}
+    : impl_(std::make_unique<Impl>(parent)) {}
 
 ConstIntBoundAnalyzer::~ConstIntBoundAnalyzer() = default;
 
diff --git a/src/arith/iter_affine_map.cc b/src/arith/iter_affine_map.cc
index af52fb78d0..aac16ec945 100644
--- a/src/arith/iter_affine_map.cc
+++ b/src/arith/iter_affine_map.cc
@@ -345,9 +345,7 @@ class IterMapRewriter : public tvm::ExprMutator {
       }
       return value;
     } catch (ffi::Error& error) {
-      if (const auto* node = input.as<ffi::Object>()) {
-        ffi::details::UpdateVisitErrorContext(error, 
ffi::GetRef<ffi::ObjectRef>(node));
-      }
+      ffi::details::UpdateVisitErrorContext(error, input);
       throw;
     }
   }
@@ -2184,8 +2182,7 @@ class IterMapToExprNormalizer : public tvm::ExprMutator {
     if (!expr->args.unique()) args_mode = InplaceMode::kDisallow;
     // Borrow stored elements: an owning typed iterator would suppress 
in-place mutation.
     for (const ffi::Any& arg : *expr->args.GetArrayObj()) {
-      res += ffi::details::AnyUnsafe::MoveFromAnyAfterCheck<PrimExpr>(
-          Mutate(arg, args_mode).ValueOrUnchanged(arg));
+      res += Mutate(arg, 
args_mode).ValueOrUnchanged(arg).as_or_throw<PrimExpr>();
     }
     res += expr->base;
     return res;
@@ -2446,17 +2443,16 @@ class SubspaceDivider {
     if (need_predicate) {
       // if we have a predicate on this sum expr, then we cannot divide it 
into Y*E+X
       // it should either be Y*1+0 or 0*E(X)+X
-      auto converter = ffi::make_object<IterMapToExprNormalizer>(analyzer_);
       if (inner_args.empty()) {
         // Y*1+0
-        PrimExpr converted =
-            
converter->Mutate(outer_source).ValueOrUnchanged(std::as_const(outer_source));
+        auto converter = ffi::make_object<IterMapToExprNormalizer>(analyzer_);
+        PrimExpr converted = 
converter->Mutate(outer_source).ValueOrUnchanged(outer_source);
         outer_preds_ = outer_preds_ && (converted < mark_extent);
         return DivisionResult::Outer(outer_source, mark_extent);
       } else if (outer_args.empty()) {
         // 0*E(X)+X
-        PrimExpr converted =
-            
converter->Mutate(inner_source).ValueOrUnchanged(std::as_const(inner_source));
+        auto converter = ffi::make_object<IterMapToExprNormalizer>(analyzer_);
+        PrimExpr converted = 
converter->Mutate(inner_source).ValueOrUnchanged(inner_source);
         inner_preds_ = inner_preds_ && (converted < mark_extent);
         return DivisionResult::Inner(inner_source, mark_extent);
       } else {
diff --git a/src/arith/modular_set.cc b/src/arith/modular_set.cc
index 97097802c4..6b8de6f36f 100644
--- a/src/arith/modular_set.cc
+++ b/src/arith/modular_set.cc
@@ -98,8 +98,7 @@ struct ModularSetAnalyzer::Entry {
   }
 };
 
-class ModularSetAnalyzer::Impl : public ffi::Object,
-                                 public 
tvm::ExprFunctor<ModularSetAnalyzer::Entry(const Expr&)> {
+class ModularSetAnalyzer::Impl : public 
tvm::ExprFunctor<ModularSetAnalyzer::Entry(const Expr&)> {
  public:
   explicit Impl(AnalyzerObj* parent) : parent_(parent) {}
 
@@ -410,7 +409,7 @@ std::function<void()> 
ModularSetAnalyzer::EnterConstraint(const PrimExpr& constr
 }
 
 ModularSetAnalyzer::ModularSetAnalyzer(AnalyzerObj* parent)
-    : impl_(ffi::make_object<Impl>(parent)) {}
+    : impl_(std::make_unique<Impl>(parent)) {}
 
 ModularSetAnalyzer::~ModularSetAnalyzer() = default;
 
diff --git a/src/arith/rewrite_simplify.cc b/src/arith/rewrite_simplify.cc
index 9091310286..2a36f32e4e 100644
--- a/src/arith/rewrite_simplify.cc
+++ b/src/arith/rewrite_simplify.cc
@@ -1935,7 +1935,6 @@ UnchangedOr<PrimExpr> 
RewriteSimplifier::Impl::Mutate_(const prim::LENode* op,
   PrimExpr ret =
       SimplifierBase::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<PrimExpr>(op));
   op = ret.as<prim::LENode>();
-  TVM_FFI_ICHECK(op);
 
   if (auto const_res = TryConstFold<prim::LE>(op->a, op->b)) return 
*std::move(const_res);
   if (auto match = TryMatchLiteralConstraint(ret)) return match.value();
@@ -2225,8 +2224,7 @@ UnchangedOr<PrimExpr> 
RewriteSimplifier::Impl::Mutate_(const prim::AndNode* op,
       With<ConstraintContext> context(analyzer_, constraint);
       // Keep this iteration out of place: convergence uses same_as() to 
detect updates.
       // An in-place change would preserve identity and could stop the loop 
early.
-      PrimExpr updated =
-          Mutate(to_update, 
InplaceMode::kDisallow).ValueOrUnchanged(std::as_const(to_update));
+      PrimExpr updated = Mutate(to_update, 
InplaceMode::kDisallow).ValueOrUnchanged(to_update);
 
       if (!to_update.same_as(updated)) {
         to_update = updated;
@@ -2377,8 +2375,7 @@ UnchangedOr<PrimExpr> 
RewriteSimplifier::Impl::Mutate_(const prim::OrNode* op,
       With<ConstraintContext> context(analyzer_, 
NormalizeBooleanOperators(prim::Not(constraint)));
       // Keep this iteration out of place: convergence uses same_as() to 
detect updates.
       // An in-place change would preserve identity and could stop the loop 
early.
-      PrimExpr updated =
-          Mutate(to_update, 
InplaceMode::kDisallow).ValueOrUnchanged(std::as_const(to_update));
+      PrimExpr updated = Mutate(to_update, 
InplaceMode::kDisallow).ValueOrUnchanged(to_update);
 
       if (!to_update.same_as(updated)) {
         to_update = updated;
@@ -2623,7 +2620,7 @@ PrimExpr RewriteSimplifier::operator()(const PrimExpr& 
expr) {
   int max_iter = 2;
   for (int i = 0; i < max_iter; ++i) {
     // Keep the default kDisallow: convergence requires same_as to detect 
updates.
-    PrimExpr new_expr = 
impl_->Mutate(res).ValueOrUnchanged(std::as_const(res));
+    PrimExpr new_expr = impl_->Mutate(res).ValueOrUnchanged(res);
     if (new_expr.same_as(res)) return res;
     res = new_expr;
   }
diff --git a/src/arith/simplify_base.cc b/src/arith/simplify_base.cc
index 8facc06743..1f18be369b 100644
--- a/src/arith/simplify_base.cc
+++ b/src/arith/simplify_base.cc
@@ -31,44 +31,45 @@ namespace arith {
 using detail::EnterConstraintFacts;
 
 UnchangedOr<Expr> SimplifierBase::Mutate_(const TupleNode* op, InplaceMode 
inplace_mode) {
-  auto fields = Mutate(op->fields, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
-  if (fields.UnchangedOrSameAs(op->fields)) return ffi::Unchanged();
+  auto fields_u = Mutate(op->fields, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
+  if (fields_u.UnchangedOrSameAs(op->fields)) return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
-    const_cast<TupleNode*>(op)->fields = std::move(fields).ValueUnchecked();
+    const_cast<TupleNode*>(op)->fields = std::move(fields_u).ValueUnchecked();
     return ffi::Unchanged();
   }
   auto copy = ffi::make_object<TupleNode>(*op);
-  copy->fields = std::move(fields).ValueUnchecked();
+  copy->fields = std::move(fields_u).ValueUnchecked();
   return Tuple(std::move(copy));
 }
 
 UnchangedOr<Expr> SimplifierBase::Mutate_(const TupleGetItemNode* op, 
InplaceMode inplace_mode) {
-  auto tuple = Mutate(op->tuple, inplace_mode);
-  if (tuple.UnchangedOrSameAs(op->tuple)) return ffi::Unchanged();
+  auto tuple_u = Mutate(op->tuple, inplace_mode);
+  if (tuple_u.UnchangedOrSameAs(op->tuple)) return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
-    const_cast<TupleGetItemNode*>(op)->tuple = 
std::move(tuple).ValueUnchecked();
+    const_cast<TupleGetItemNode*>(op)->tuple = 
std::move(tuple_u).ValueUnchecked();
     return ffi::Unchanged();
   }
   auto copy = ffi::make_object<TupleGetItemNode>(*op);
-  copy->tuple = std::move(tuple).ValueUnchecked();
+  copy->tuple = std::move(tuple_u).ValueUnchecked();
   return TupleGetItem(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> SimplifierBase::Mutate_(const TensorLoadNode* op, 
InplaceMode inplace_mode) {
-  auto source = Mutate(op->source, inplace_mode);
-  auto indices = Mutate(op->indices, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
-  if (source.UnchangedOrSameAs(op->source) && 
indices.UnchangedOrSameAs(op->indices)) {
+  auto source_u = Mutate(op->source, inplace_mode);
+  auto indices_u =
+      Mutate(op->indices, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
+  if (source_u.UnchangedOrSameAs(op->source) && 
indices_u.UnchangedOrSameAs(op->indices)) {
     return ffi::Unchanged();
   }
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<TensorLoadNode*>(op);
-    if (!source.IsUnchanged()) writable->source = 
std::move(source).ValueUnchecked();
-    if (!indices.IsUnchanged()) writable->indices = 
std::move(indices).ValueUnchecked();
+    if (!source_u.IsUnchanged()) writable->source = 
std::move(source_u).ValueUnchecked();
+    if (!indices_u.IsUnchanged()) writable->indices = 
std::move(indices_u).ValueUnchecked();
     return ffi::Unchanged();
   }
   auto copy = ffi::make_object<TensorLoadNode>(*op);
-  copy->source = std::move(source).ValueOrUnchanged(std::move(copy->source));
-  copy->indices = 
std::move(indices).ValueOrUnchanged(std::move(copy->indices));
+  if (!source_u.IsUnchanged()) copy->source = 
std::move(source_u).ValueUnchecked();
+  if (!indices_u.IsUnchanged()) copy->indices = 
std::move(indices_u).ValueUnchecked();
   return TensorLoad(std::move(copy));
 }
 
@@ -82,23 +83,23 @@ UnchangedOr<Expr> SimplifierBase::Mutate_(const CallNode* 
op, InplaceMode inplac
     const auto* args = op->args.GetArrayObj();
     PrimExpr cond =
         Mutate((*args)[0], 
inplace_mode_args).ValueOrUnchanged((*args)[0]).as_or_throw<PrimExpr>();
-    Expr true_value = ffi::details::AnyUnsafe::MoveFromAnyAfterCheck<Expr>(
-        constraint_scope_
-            .WithNewScope([&]() {
-              EnterConstraintFacts(&constraint_scope_.Current(), analyzer_, 
cond);
-              return Mutate((*args)[1], inplace_mode_args);
-            })
-            .ValueOrUnchanged((*args)[1]));
+    Expr true_value = constraint_scope_
+                          .WithNewScope([&]() {
+                            EnterConstraintFacts(&constraint_scope_.Current(), 
analyzer_, cond);
+                            return Mutate((*args)[1], inplace_mode_args);
+                          })
+                          .ValueOrUnchanged((*args)[1])
+                          .as_or_throw<Expr>();
     Expr false_value;
     {
       PrimExpr not_cond = prim::Not(cond);
-      false_value = ffi::details::AnyUnsafe::MoveFromAnyAfterCheck<Expr>(
-          constraint_scope_
-              .WithNewScope([&]() {
-                constraint_scope_.Current().Emplace(analyzer_, not_cond);
-                return Mutate((*args)[2], inplace_mode_args);
-              })
-              .ValueOrUnchanged((*args)[2]));
+      false_value = constraint_scope_
+                        .WithNewScope([&]() {
+                          constraint_scope_.Current().Emplace(analyzer_, 
not_cond);
+                          return Mutate((*args)[2], inplace_mode_args);
+                        })
+                        .ValueOrUnchanged((*args)[2])
+                        .as_or_throw<Expr>();
     }
     if (tirx::is_zero(cond)) return false_value;
     if (tirx::is_one(cond)) return true_value;
@@ -109,14 +110,14 @@ UnchangedOr<Expr> SimplifierBase::Mutate_(const CallNode* 
op, InplaceMode inplac
     }
     return Call(op->ty, op->op, {cond, true_value, false_value}, op->attrs, 
op->ty_args, op->span);
   }
-  auto args = Mutate(op->args, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
-  if (args.UnchangedOrSameAs(op->args)) return ffi::Unchanged();
+  auto args_u = Mutate(op->args, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
+  if (args_u.UnchangedOrSameAs(op->args)) return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
-    const_cast<CallNode*>(op)->args = std::move(args).ValueUnchecked();
+    const_cast<CallNode*>(op)->args = std::move(args_u).ValueUnchecked();
     return ffi::Unchanged();
   }
   auto copy = ffi::make_object<CallNode>(*op);
-  copy->args = std::move(args).ValueUnchecked();
+  copy->args = std::move(args_u).ValueUnchecked();
   return Call(std::move(copy));
 }
 
diff --git a/src/arith/simplify_base.h b/src/arith/simplify_base.h
index 56b3930e36..eaafa1fa06 100644
--- a/src/arith/simplify_base.h
+++ b/src/arith/simplify_base.h
@@ -47,8 +47,6 @@ class SimplifierBase : public tvm::ExprMutator {
   using Parent = tvm::ExprMutator;
   explicit SimplifierBase(AnalyzerObj* analyzer) : analyzer_(analyzer) {}
 
-  virtual ~SimplifierBase() = default;
-
   using Parent::Mutate_;
 
   UnchangedOr<Expr> Mutate_(const OpaqueExprNode* op, InplaceMode 
inplace_mode) override {
diff --git a/src/arith/solve_linear_inequality.cc 
b/src/arith/solve_linear_inequality.cc
index 839616090e..4bc103708a 100644
--- a/src/arith/solve_linear_inequality.cc
+++ b/src/arith/solve_linear_inequality.cc
@@ -230,10 +230,10 @@ PartialSolvedInequalities SolveLinearInequalities(const 
IntConstraints& system_t
   std::vector<PrimExpr> rest;
 
   // Simplify each inequality into the form `expr <= 0` and add to current 
formulas
+  auto normalizer = ffi::make_object<NormalizeComparisons>();
   for (const PrimExpr& ineq : system_to_solve->relations) {
     PrimExpr simplified = analyzer->Simplify(ineq, 
kSimplifyRewriteCanonicalRewrite);
-    PrimExpr normalized =
-        
ffi::make_object<NormalizeComparisons>()->Mutate(simplified).ValueOrUnchanged(simplified);
+    PrimExpr normalized = 
normalizer->Mutate(simplified).ValueOrUnchanged(simplified);
     AddInequality(&current_ineq_set_to_solve, normalized, analyzer.get());
   }
 
@@ -275,8 +275,7 @@ PartialSolvedInequalities SolveLinearInequalities(const 
IntConstraints& system_t
         // to help simplify things like (((y + 10) - (-1*(y - 20))) <= 0) => y 
- 5 <= 0
         // with steps = 2 it's (y*2) - 10 <= 0
         new_ineq = analyzer->Simplify(new_ineq, 
kSimplifyRewriteCanonicalRewrite);
-        new_ineq =
-            
ffi::make_object<NormalizeComparisons>()->Mutate(new_ineq).ValueOrUnchanged(new_ineq);
+        new_ineq = normalizer->Mutate(new_ineq).ValueOrUnchanged(new_ineq);
         AddInequality(&next_ineq_set_to_solve, new_ineq, analyzer.get());
       }
     }
diff --git a/src/arith/z3_prover.cc b/src/arith/z3_prover.cc
index f6bf51cc12..d94c7544f3 100644
--- a/src/arith/z3_prover.cc
+++ b/src/arith/z3_prover.cc
@@ -108,7 +108,7 @@ void EnterZ3ContextScope() {}
 
 void ExitZ3ContextScope() {}
 
-class Z3Prover::Impl : public ffi::Object, tvm::ExprFunctor<z3::expr(const 
Expr&)> {
+class Z3Prover::Impl : tvm::ExprFunctor<z3::expr(const Expr&)> {
  public:
   using Base = tvm::ExprFunctor<z3::expr(const Expr&)>;
   using Self = Z3Prover::Impl;
@@ -1014,7 +1014,7 @@ TVM_DLL int64_t Z3Prover::CountSatisfyingValues(const 
Var& var, int64_t max_coun
                                                 int64_t min_consecutive) {
   return impl_->CountSatisfyingValues(var, max_count, min_consecutive);
 }
-Z3Prover::Z3Prover(AnalyzerObj* parent) : 
impl_(ffi::make_object<Impl>(parent)) {}
+Z3Prover::Z3Prover(AnalyzerObj* parent) : 
impl_(std::make_unique<Impl>(parent)) {}
 TVM_DLL Z3Prover::~Z3Prover() = default;
 
 }  // namespace tvm::arith
@@ -1038,7 +1038,7 @@ void ExitZ3ContextScope() {}
 
 // Stub implementation used when Z3 support is not built. All proving queries
 // conservatively report "cannot prove" while keeping the public API available.
-class Z3Prover::Impl : public ffi::Object {};
+class Z3Prover::Impl {};
 
 TVM_DLL bool Z3Prover::IsEnabled() const { return false; }
 TVM_DLL bool Z3Prover::CanProve(const PrimExpr& expr) { return false; }
diff --git a/src/ir/expr.cc b/src/ir/expr.cc
index e2fb8b30ef..37bd552959 100644
--- a/src/ir/expr.cc
+++ b/src/ir/expr.cc
@@ -53,13 +53,13 @@ TVMFFIAny OpaqueExprVisit(ffi::StructuralVisitorObj* 
visitor, ffi::AnyView value
 TVMFFIAny OpaqueExprMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView 
value) noexcept {
   const OpaqueExprNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
OpaqueExprNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty));
-  if (mapped_ty.UnchangedOrSameAs(self->ty)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<OpaqueExprNode> copy = 
ffi::make_object<OpaqueExprNode>(*self);
-  copy->ty = std::move(mapped_ty).ValueOrUnchanged(std::move(copy->ty));
+  if (!mapped_ty_u.IsUnchanged()) copy->ty = 
std::move(mapped_ty_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -67,12 +67,12 @@ TVMFFIAny 
OpaqueExprMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
                                        ffi::AnyView value) noexcept {
   OpaqueExprNode* self = const_cast<OpaqueExprNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
OpaqueExprNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty, 
ffi::InplaceMode::kAllow));
-  if (mapped_ty.UnchangedOrSameAs(self->ty)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_ty.IsUnchanged()) self->ty = 
std::move(mapped_ty).ValueUnchecked();
+  if (!mapped_ty_u.IsUnchanged()) self->ty = 
std::move(mapped_ty_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -88,20 +88,20 @@ TVMFFIAny TensorLoadVisit(ffi::StructuralVisitorObj* 
visitor, ffi::AnyView value
 TVMFFIAny TensorLoadMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView 
value) noexcept {
   const TensorLoadNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TensorLoadNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, mapped_source,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, mapped_source_u,
                                     mutator->MutateExpected(self->source));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<PrimExpr>>, 
mapped_indices,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<PrimExpr>>, 
mapped_indices_u,
                                     mutator->MutateExpected(self->indices));
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_source.UnchangedOrSameAs(self->source) &&
-      mapped_indices.UnchangedOrSameAs(self->indices)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_source_u.UnchangedOrSameAs(self->source) &&
+      mapped_indices_u.UnchangedOrSameAs(self->indices)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<TensorLoadNode> copy = 
ffi::make_object<TensorLoadNode>(*self);
-  copy->ty = std::move(mapped_ty).ValueOrUnchanged(std::move(copy->ty));
-  copy->source = 
std::move(mapped_source).ValueOrUnchanged(std::move(copy->source));
-  copy->indices = 
std::move(mapped_indices).ValueOrUnchanged(std::move(copy->indices));
+  if (!mapped_ty_u.IsUnchanged()) copy->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_source_u.IsUnchanged()) copy->source = 
std::move(mapped_source_u).ValueUnchecked();
+  if (!mapped_indices_u.IsUnchanged()) copy->indices = 
std::move(mapped_indices_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -109,21 +109,21 @@ TVMFFIAny 
TensorLoadMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
                                        ffi::AnyView value) noexcept {
   TensorLoadNode* self = const_cast<TensorLoadNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TensorLoadNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty, 
ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<Expr>, mapped_source,
+      ffi::UnchangedOr<Expr>, mapped_source_u,
       mutator->MutateExpected(self->source, ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<ffi::Array<PrimExpr>>, mapped_indices,
+      ffi::UnchangedOr<ffi::Array<PrimExpr>>, mapped_indices_u,
       mutator->MutateExpected(self->indices, ffi::InplaceMode::kAllow));
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_source.UnchangedOrSameAs(self->source) &&
-      mapped_indices.UnchangedOrSameAs(self->indices)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_source_u.UnchangedOrSameAs(self->source) &&
+      mapped_indices_u.UnchangedOrSameAs(self->indices)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_ty.IsUnchanged()) self->ty = 
std::move(mapped_ty).ValueUnchecked();
-  if (!mapped_source.IsUnchanged()) self->source = 
std::move(mapped_source).ValueUnchecked();
-  if (!mapped_indices.IsUnchanged()) self->indices = 
std::move(mapped_indices).ValueUnchecked();
+  if (!mapped_ty_u.IsUnchanged()) self->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_source_u.IsUnchanged()) self->source = 
std::move(mapped_source_u).ValueUnchecked();
+  if (!mapped_indices_u.IsUnchanged()) self->indices = 
std::move(mapped_indices_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -138,32 +138,32 @@ TVMFFIAny TupleVisit(ffi::StructuralVisitorObj* visitor, 
ffi::AnyView value) noe
 TVMFFIAny TupleMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView value) 
noexcept {
   const TupleNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TupleNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Expr>>, 
mapped_fields,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Expr>>, 
mapped_fields_u,
                                     mutator->MutateExpected(self->fields));
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_fields.UnchangedOrSameAs(self->fields)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_fields_u.UnchangedOrSameAs(self->fields)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<TupleNode> copy = ffi::make_object<TupleNode>(*self);
-  copy->ty = std::move(mapped_ty).ValueOrUnchanged(std::move(copy->ty));
-  copy->fields = 
std::move(mapped_fields).ValueOrUnchanged(std::move(copy->fields));
+  if (!mapped_ty_u.IsUnchanged()) copy->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_fields_u.IsUnchanged()) copy->fields = 
std::move(mapped_fields_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
 TVMFFIAny TupleMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noexcept {
   TupleNode* self = const_cast<TupleNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TupleNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty, 
ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<ffi::Array<Expr>>, mapped_fields,
+      ffi::UnchangedOr<ffi::Array<Expr>>, mapped_fields_u,
       mutator->MutateExpected(self->fields, ffi::InplaceMode::kAllow));
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_fields.UnchangedOrSameAs(self->fields)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_fields_u.UnchangedOrSameAs(self->fields)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_ty.IsUnchanged()) self->ty = 
std::move(mapped_ty).ValueUnchecked();
-  if (!mapped_fields.IsUnchanged()) self->fields = 
std::move(mapped_fields).ValueUnchecked();
+  if (!mapped_ty_u.IsUnchanged()) self->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_fields_u.IsUnchanged()) self->fields = 
std::move(mapped_fields_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -180,16 +180,16 @@ TVMFFIAny TupleGetItemMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyView va
   // skips: index
   const TupleGetItemNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TupleGetItemNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, mapped_tuple,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, mapped_tuple_u,
                                     mutator->MutateExpected(self->tuple));
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_tuple.UnchangedOrSameAs(self->tuple)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_tuple_u.UnchangedOrSameAs(self->tuple)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<TupleGetItemNode> copy = 
ffi::make_object<TupleGetItemNode>(*self);
-  copy->ty = std::move(mapped_ty).ValueOrUnchanged(std::move(copy->ty));
-  copy->tuple = 
std::move(mapped_tuple).ValueOrUnchanged(std::move(copy->tuple));
+  if (!mapped_ty_u.IsUnchanged()) copy->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_tuple_u.IsUnchanged()) copy->tuple = 
std::move(mapped_tuple_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -198,15 +198,15 @@ TVMFFIAny 
TupleGetItemMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   // skips: index
   TupleGetItemNode* self = const_cast<TupleGetItemNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TupleGetItemNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                     mutator->MutateExpected(self->ty, 
ffi::InplaceMode::kAllow));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, mapped_tuple,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, mapped_tuple_u,
                                     mutator->MutateExpected(self->tuple, 
ffi::InplaceMode::kAllow));
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_tuple.UnchangedOrSameAs(self->tuple)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_tuple_u.UnchangedOrSameAs(self->tuple)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_ty.IsUnchanged()) self->ty = 
std::move(mapped_ty).ValueUnchecked();
-  if (!mapped_tuple.IsUnchanged()) self->tuple = 
std::move(mapped_tuple).ValueUnchecked();
+  if (!mapped_ty_u.IsUnchanged()) self->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_tuple_u.IsUnchanged()) self->tuple = 
std::move(mapped_tuple_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -251,32 +251,34 @@ TVMFFIAny RangeVisit(ffi::StructuralVisitorObj* visitor, 
ffi::AnyView value) noe
 TVMFFIAny RangeMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView value) 
noexcept {
   const RangeNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
RangeNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_min,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_min_u,
                                     mutator->MutateExpected(self->min));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_extent,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_extent_u,
                                     mutator->MutateExpected(self->extent));
-  if (mapped_min.UnchangedOrSameAs(self->min) && 
mapped_extent.UnchangedOrSameAs(self->extent)) {
+  if (mapped_min_u.UnchangedOrSameAs(self->min) &&
+      mapped_extent_u.UnchangedOrSameAs(self->extent)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<RangeNode> copy = ffi::make_object<RangeNode>(*self);
-  copy->min = std::move(mapped_min).ValueOrUnchanged(std::move(copy->min));
-  copy->extent = 
std::move(mapped_extent).ValueOrUnchanged(std::move(copy->extent));
+  if (!mapped_min_u.IsUnchanged()) copy->min = 
std::move(mapped_min_u).ValueUnchecked();
+  if (!mapped_extent_u.IsUnchanged()) copy->extent = 
std::move(mapped_extent_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
 TVMFFIAny RangeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noexcept {
   RangeNode* self = const_cast<RangeNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
RangeNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_min,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_min_u,
                                     mutator->MutateExpected(self->min, 
ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<PrimExpr>, mapped_extent,
+      ffi::UnchangedOr<PrimExpr>, mapped_extent_u,
       mutator->MutateExpected(self->extent, ffi::InplaceMode::kAllow));
-  if (mapped_min.UnchangedOrSameAs(self->min) && 
mapped_extent.UnchangedOrSameAs(self->extent)) {
+  if (mapped_min_u.UnchangedOrSameAs(self->min) &&
+      mapped_extent_u.UnchangedOrSameAs(self->extent)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_min.IsUnchanged()) self->min = 
std::move(mapped_min).ValueUnchecked();
-  if (!mapped_extent.IsUnchanged()) self->extent = 
std::move(mapped_extent).ValueUnchecked();
+  if (!mapped_min_u.IsUnchanged()) self->min = 
std::move(mapped_min_u).ValueUnchecked();
+  if (!mapped_extent_u.IsUnchanged()) self->extent = 
std::move(mapped_extent_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -314,7 +316,7 @@ TVMFFIAny VarMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noex
   if (mutator->def_region_kind() == kTVMFFIDefRegionKindNone) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  ffi::UnchangedOr<ffi::Any> result = ffi::Unchanged();
+  ffi::UnchangedOr<ffi::Any> result_u = ffi::Unchanged();
   ffi::Any mapped_value = ffi::Unchanged();
   // A PrimType carries only a dtype, so it has nothing to substitute.  Broad 
callbacks do not see
   // this skipped field; dynamically typed Vars still descend through the Type 
value.
@@ -325,22 +327,22 @@ TVMFFIAny VarMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noex
             ? mutator->WithDefRegionKind(kTVMFFIDefRegionKindNone,
                                          [&]() { return 
mutator->MutateExpected(self->ty); })
             : mutator->MutateExpected(self->ty);
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                       std::move(mapped_ty_result));
-    if (!mapped_ty.UnchangedOrSameAs(self->ty)) {
+    if (!mapped_ty_u.UnchangedOrSameAs(self->ty)) {
       ffi::ObjectPtr<VarNode> copy = ffi::make_object<VarNode>(*self);
-      copy->ty = std::move(mapped_ty).ValueUnchecked();
+      copy->ty = std::move(mapped_ty_u).ValueUnchecked();
       mapped_value = ffi::Any(std::move(copy));
-      result = mapped_value;
+      result_u = mapped_value;
     }
   }
-  if (!result.IsUnchanged() || mutator->def_region_kind() == 
kTVMFFIDefRegionKindPattern) {
+  if (!result_u.IsUnchanged() || mutator->def_region_kind() == 
kTVMFFIDefRegionKindPattern) {
     auto set_result = mutator->VarRemapSetExpected(value, mapped_value);
     if (TVM_FFI_PREDICT_FALSE(set_result.is_err())) {
       return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(set_result).error()));
     }
   }
-  return ffi::details::UnchangedOrUnsafe::MoveToTVMFFIAny(std::move(result));
+  return ffi::details::UnchangedOrUnsafe::MoveToTVMFFIAny(std::move(result_u));
 }
 
 TVMFFIAny VarMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noexcept {
@@ -356,7 +358,7 @@ TVMFFIAny VarMaybeInplaceMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyView
   if (mutator->def_region_kind() == kTVMFFIDefRegionKindNone) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  ffi::UnchangedOr<ffi::Any> result = ffi::Unchanged();
+  ffi::UnchangedOr<ffi::Any> result_u = ffi::Unchanged();
   ffi::Any mapped_value = ffi::Unchanged();
   // A PrimType carries only a dtype, so it has nothing to substitute.  Broad 
callbacks do not see
   // this skipped field; dynamically typed Vars still descend through the Type 
value.
@@ -368,21 +370,21 @@ TVMFFIAny 
VarMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView
                   kTVMFFIDefRegionKindNone,
                   [&]() { return mutator->MutateExpected(self->ty, 
ffi::InplaceMode::kAllow); })
             : mutator->MutateExpected(self->ty, ffi::InplaceMode::kAllow);
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ty_u,
                                       std::move(mapped_ty_result));
-    if (!mapped_ty.UnchangedOrSameAs(self->ty)) {
-      self->ty = std::move(mapped_ty).ValueUnchecked();
+    if (!mapped_ty_u.UnchangedOrSameAs(self->ty)) {
+      self->ty = std::move(mapped_ty_u).ValueUnchecked();
       mapped_value = ffi::Any(self);
-      result = mapped_value;
+      result_u = mapped_value;
     }
   }
-  if (!result.IsUnchanged() || mutator->def_region_kind() == 
kTVMFFIDefRegionKindPattern) {
+  if (!result_u.IsUnchanged() || mutator->def_region_kind() == 
kTVMFFIDefRegionKindPattern) {
     auto set_result = mutator->VarRemapSetExpected(value, mapped_value);
     if (TVM_FFI_PREDICT_FALSE(set_result.is_err())) {
       return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(set_result).error()));
     }
   }
-  return ffi::details::UnchangedOrUnsafe::MoveToTVMFFIAny(std::move(result));
+  return ffi::details::UnchangedOrUnsafe::MoveToTVMFFIAny(std::move(result_u));
 }
 
 TVMFFIAny GlobalVarVisit(ffi::StructuralVisitorObj*, ffi::AnyView) noexcept {
@@ -430,40 +432,40 @@ TVMFFIAny CallMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noe
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
CallNode>(value);
   // A PrimType carries only a dtype, so it has nothing to substitute.  Broad 
callbacks do not see
   // this skipped field; dynamically typed Call results still descend through 
the Type value.
-  ffi::UnchangedOr<Type> mapped_ty = ffi::Unchanged();
+  ffi::UnchangedOr<Type> mapped_ty_u = ffi::Unchanged();
   if (!self->ty.as<PrimTypeNode>()) {
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, descended_ty,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, descended_ty_u,
                                       mutator->MutateExpected(self->ty));
-    mapped_ty = std::move(descended_ty);
+    mapped_ty_u = std::move(descended_ty_u);
   }
   // An Op is an interned registry singleton, so it has nothing to substitute. 
 Broad callbacks do
   // not see this skipped field; function-valued Call operators still descend 
through the Expr.
-  ffi::UnchangedOr<Expr> mapped_op = ffi::Unchanged();
+  ffi::UnchangedOr<Expr> mapped_op_u = ffi::Unchanged();
   if (!self->op.as<OpNode>()) {
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, descended_op,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, descended_op_u,
                                       mutator->MutateExpected(self->op));
-    mapped_op = std::move(descended_op);
+    mapped_op_u = std::move(descended_op_u);
   }
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Expr>>, 
mapped_args,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Expr>>, 
mapped_args_u,
                                     mutator->MutateExpected(self->args));
   // An empty ty_args has no element to substitute.  Broad callbacks do not 
see the empty
   // container; nonempty type arguments retain normal container descent and 
callback behavior.
-  ffi::UnchangedOr<ffi::Array<Type>> mapped_ty_args = ffi::Unchanged();
+  ffi::UnchangedOr<ffi::Array<Type>> mapped_ty_args_u = ffi::Unchanged();
   if (!self->ty_args.empty()) {
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, 
descended_ty_args,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, 
descended_ty_args_u,
                                       mutator->MutateExpected(self->ty_args));
-    mapped_ty_args = std::move(descended_ty_args);
+    mapped_ty_args_u = std::move(descended_ty_args_u);
   }
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_op.UnchangedOrSameAs(self->op) &&
-      mapped_args.UnchangedOrSameAs(self->args) &&
-      mapped_ty_args.UnchangedOrSameAs(self->ty_args)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_op_u.UnchangedOrSameAs(self->op) &&
+      mapped_args_u.UnchangedOrSameAs(self->args) &&
+      mapped_ty_args_u.UnchangedOrSameAs(self->ty_args)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<CallNode> copy = ffi::make_object<CallNode>(*self);
-  copy->ty = std::move(mapped_ty).ValueOrUnchanged(std::move(copy->ty));
-  copy->op = std::move(mapped_op).ValueOrUnchanged(std::move(copy->op));
-  copy->args = std::move(mapped_args).ValueOrUnchanged(std::move(copy->args));
-  copy->ty_args = 
std::move(mapped_ty_args).ValueOrUnchanged(std::move(copy->ty_args));
+  if (!mapped_ty_u.IsUnchanged()) copy->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_op_u.IsUnchanged()) copy->op = 
std::move(mapped_op_u).ValueUnchecked();
+  if (!mapped_args_u.IsUnchanged()) copy->args = 
std::move(mapped_args_u).ValueUnchecked();
+  if (!mapped_ty_args_u.IsUnchanged()) copy->ty_args = 
std::move(mapped_ty_args_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -473,40 +475,40 @@ TVMFFIAny 
CallMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyVie
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
CallNode>(value));
   // A PrimType carries only a dtype, so it has nothing to substitute.  Broad 
callbacks do not see
   // this skipped field; dynamically typed Call results still descend through 
the Type value.
-  ffi::UnchangedOr<Type> mapped_ty = ffi::Unchanged();
+  ffi::UnchangedOr<Type> mapped_ty_u = ffi::Unchanged();
   if (!self->ty.as<PrimTypeNode>()) {
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, descended_ty,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, descended_ty_u,
                                       mutator->MutateExpected(self->ty, 
ffi::InplaceMode::kAllow));
-    mapped_ty = std::move(descended_ty);
+    mapped_ty_u = std::move(descended_ty_u);
   }
   // An Op is an interned registry singleton, so it has nothing to substitute. 
 Broad callbacks do
   // not see this skipped field; function-valued Call operators still descend 
through the Expr.
-  ffi::UnchangedOr<Expr> mapped_op = ffi::Unchanged();
+  ffi::UnchangedOr<Expr> mapped_op_u = ffi::Unchanged();
   if (!self->op.as<OpNode>()) {
-    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, descended_op,
+    TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Expr>, descended_op_u,
                                       mutator->MutateExpected(self->op, 
ffi::InplaceMode::kAllow));
-    mapped_op = std::move(descended_op);
+    mapped_op_u = std::move(descended_op_u);
   }
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Expr>>, 
mapped_args,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Expr>>, 
mapped_args_u,
                                     mutator->MutateExpected(self->args, 
ffi::InplaceMode::kAllow));
   // An empty ty_args has no element to substitute.  Broad callbacks do not 
see the empty
   // container; nonempty type arguments retain normal container descent and 
callback behavior.
-  ffi::UnchangedOr<ffi::Array<Type>> mapped_ty_args = ffi::Unchanged();
+  ffi::UnchangedOr<ffi::Array<Type>> mapped_ty_args_u = ffi::Unchanged();
   if (!self->ty_args.empty()) {
     TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-        ffi::UnchangedOr<ffi::Array<Type>>, descended_ty_args,
+        ffi::UnchangedOr<ffi::Array<Type>>, descended_ty_args_u,
         mutator->MutateExpected(self->ty_args, ffi::InplaceMode::kAllow));
-    mapped_ty_args = std::move(descended_ty_args);
+    mapped_ty_args_u = std::move(descended_ty_args_u);
   }
-  if (mapped_ty.UnchangedOrSameAs(self->ty) && 
mapped_op.UnchangedOrSameAs(self->op) &&
-      mapped_args.UnchangedOrSameAs(self->args) &&
-      mapped_ty_args.UnchangedOrSameAs(self->ty_args)) {
+  if (mapped_ty_u.UnchangedOrSameAs(self->ty) && 
mapped_op_u.UnchangedOrSameAs(self->op) &&
+      mapped_args_u.UnchangedOrSameAs(self->args) &&
+      mapped_ty_args_u.UnchangedOrSameAs(self->ty_args)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_ty.IsUnchanged()) self->ty = 
std::move(mapped_ty).ValueUnchecked();
-  if (!mapped_op.IsUnchanged()) self->op = 
std::move(mapped_op).ValueUnchecked();
-  if (!mapped_args.IsUnchanged()) self->args = 
std::move(mapped_args).ValueUnchecked();
-  if (!mapped_ty_args.IsUnchanged()) self->ty_args = 
std::move(mapped_ty_args).ValueUnchecked();
+  if (!mapped_ty_u.IsUnchanged()) self->ty = 
std::move(mapped_ty_u).ValueUnchecked();
+  if (!mapped_op_u.IsUnchanged()) self->op = 
std::move(mapped_op_u).ValueUnchecked();
+  if (!mapped_args_u.IsUnchanged()) self->args = 
std::move(mapped_args_u).ValueUnchecked();
+  if (!mapped_ty_args_u.IsUnchanged()) self->ty_args = 
std::move(mapped_ty_args_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
diff --git a/src/ir/expr_functor.cc b/src/ir/expr_functor.cc
index 637a7ccdb0..9ddd23270d 100644
--- a/src/ir/expr_functor.cc
+++ b/src/ir/expr_functor.cc
@@ -315,77 +315,72 @@ void ExprMutator::InitVTable(VTable* vtable) {
 }
 
 UnchangedOr<Expr> ExprMutator::Mutate_(const OpaqueExprNode* node, InplaceMode 
inplace_mode) {
-  auto ty = this->Mutate(node->ty, 
inplace_mode).as_or_throw<UnchangedOr<Type>>();
-  if (ty.UnchangedOrSameAs(node->ty)) return ffi::Unchanged();
+  auto ty_u = Mutate(node->ty, inplace_mode).as_or_throw<UnchangedOr<Type>>();
+  if (ty_u.UnchangedOrSameAs(node->ty)) return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<OpaqueExprNode*>(node);
-    if (!ty.IsUnchanged()) writable->ty = std::move(ty).ValueUnchecked();
+    if (!ty_u.IsUnchanged()) writable->ty = std::move(ty_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<OpaqueExprNode>(*node);
-    copy->ty = std::move(ty).ValueOrUnchanged(std::move(copy->ty));
-    return Expr(std::move(copy));
   }
+  auto copy = ffi::make_object<OpaqueExprNode>(*node);
+  if (!ty_u.IsUnchanged()) copy->ty = std::move(ty_u).ValueUnchecked();
+  return Expr(std::move(copy));
 }
 
 UnchangedOr<Expr> ExprMutator::Mutate_(const TupleNode* node, InplaceMode 
inplace_mode) {
-  auto ty = this->Mutate(node->ty, 
inplace_mode).as_or_throw<UnchangedOr<Type>>();
-  auto fields =
-      this->Mutate(node->fields, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
-  if (ty.UnchangedOrSameAs(node->ty) && fields.UnchangedOrSameAs(node->fields))
+  auto ty_u = Mutate(node->ty, inplace_mode).as_or_throw<UnchangedOr<Type>>();
+  auto fields_u = Mutate(node->fields, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
+  if (ty_u.UnchangedOrSameAs(node->ty) && 
fields_u.UnchangedOrSameAs(node->fields))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<TupleNode*>(node);
-    if (!ty.IsUnchanged()) writable->ty = std::move(ty).ValueUnchecked();
-    if (!fields.IsUnchanged()) writable->fields = 
std::move(fields).ValueUnchecked();
+    if (!ty_u.IsUnchanged()) writable->ty = std::move(ty_u).ValueUnchecked();
+    if (!fields_u.IsUnchanged()) writable->fields = 
std::move(fields_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<TupleNode>(*node);
-    copy->ty = std::move(ty).ValueOrUnchanged(std::move(copy->ty));
-    copy->fields = std::move(fields).ValueOrUnchanged(std::move(copy->fields));
-    return Expr(std::move(copy));
   }
+  auto copy = ffi::make_object<TupleNode>(*node);
+  if (!ty_u.IsUnchanged()) copy->ty = std::move(ty_u).ValueUnchecked();
+  if (!fields_u.IsUnchanged()) copy->fields = 
std::move(fields_u).ValueUnchecked();
+  return Expr(std::move(copy));
 }
 
 UnchangedOr<Expr> ExprMutator::Mutate_(const TupleGetItemNode* node, 
InplaceMode inplace_mode) {
-  auto ty = this->Mutate(node->ty, 
inplace_mode).as_or_throw<UnchangedOr<Type>>();
-  auto tuple = Mutate(node->tuple, inplace_mode);
-  if (ty.UnchangedOrSameAs(node->ty) && tuple.UnchangedOrSameAs(node->tuple))
+  auto ty_u = Mutate(node->ty, inplace_mode).as_or_throw<UnchangedOr<Type>>();
+  auto tuple_u = Mutate(node->tuple, inplace_mode);
+  if (ty_u.UnchangedOrSameAs(node->ty) && 
tuple_u.UnchangedOrSameAs(node->tuple))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<TupleGetItemNode*>(node);
-    if (!ty.IsUnchanged()) writable->ty = std::move(ty).ValueUnchecked();
-    if (!tuple.IsUnchanged()) writable->tuple = 
std::move(tuple).ValueUnchecked();
+    if (!ty_u.IsUnchanged()) writable->ty = std::move(ty_u).ValueUnchecked();
+    if (!tuple_u.IsUnchanged()) writable->tuple = 
std::move(tuple_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<TupleGetItemNode>(*node);
-    copy->ty = std::move(ty).ValueOrUnchanged(std::move(copy->ty));
-    copy->tuple = std::move(tuple).ValueOrUnchanged(std::move(copy->tuple));
-    return Expr(std::move(copy));
   }
+  auto copy = ffi::make_object<TupleGetItemNode>(*node);
+  if (!ty_u.IsUnchanged()) copy->ty = std::move(ty_u).ValueUnchecked();
+  if (!tuple_u.IsUnchanged()) copy->tuple = 
std::move(tuple_u).ValueUnchecked();
+  return Expr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const TensorLoadNode* node, 
InplaceMode inplace_mode) {
-  auto ty = this->Mutate(node->ty, 
inplace_mode).as_or_throw<UnchangedOr<Type>>();
-  auto source = Mutate(node->source, inplace_mode);
-  auto indices =
-      this->Mutate(node->indices, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
-  if (ty.UnchangedOrSameAs(node->ty) && source.UnchangedOrSameAs(node->source) 
&&
-      indices.UnchangedOrSameAs(node->indices))
+  auto ty_u = Mutate(node->ty, inplace_mode).as_or_throw<UnchangedOr<Type>>();
+  auto source_u = Mutate(node->source, inplace_mode);
+  auto indices_u =
+      Mutate(node->indices, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
+  if (ty_u.UnchangedOrSameAs(node->ty) && 
source_u.UnchangedOrSameAs(node->source) &&
+      indices_u.UnchangedOrSameAs(node->indices))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<TensorLoadNode*>(node);
-    if (!ty.IsUnchanged()) writable->ty = std::move(ty).ValueUnchecked();
-    if (!source.IsUnchanged()) writable->source = 
std::move(source).ValueUnchecked();
-    if (!indices.IsUnchanged()) writable->indices = 
std::move(indices).ValueUnchecked();
+    if (!ty_u.IsUnchanged()) writable->ty = std::move(ty_u).ValueUnchecked();
+    if (!source_u.IsUnchanged()) writable->source = 
std::move(source_u).ValueUnchecked();
+    if (!indices_u.IsUnchanged()) writable->indices = 
std::move(indices_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<TensorLoadNode>(*node);
-    copy->ty = std::move(ty).ValueOrUnchanged(std::move(copy->ty));
-    copy->source = std::move(source).ValueOrUnchanged(std::move(copy->source));
-    copy->indices = 
std::move(indices).ValueOrUnchanged(std::move(copy->indices));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<TensorLoadNode>(*node);
+  if (!ty_u.IsUnchanged()) copy->ty = std::move(ty_u).ValueUnchecked();
+  if (!source_u.IsUnchanged()) copy->source = 
std::move(source_u).ValueUnchecked();
+  if (!indices_u.IsUnchanged()) copy->indices = 
std::move(indices_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<Expr> ExprMutator::Mutate_(const GlobalVarNode* node, InplaceMode 
inplace_mode) {
@@ -394,41 +389,36 @@ UnchangedOr<Expr> ExprMutator::Mutate_(const 
GlobalVarNode* node, InplaceMode in
 }
 
 UnchangedOr<Expr> ExprMutator::Mutate_(const CallNode* node, InplaceMode 
inplace_mode) {
-  UnchangedOr<Type> ty = ffi::Unchanged();
+  UnchangedOr<Type> ty_u = ffi::Unchanged();
   if (!node->ty.as<PrimTypeNode>()) {
-    auto mapped_ty = this->Mutate(node->ty, 
inplace_mode).as_or_throw<UnchangedOr<Type>>();
-    ty = std::move(mapped_ty);
+    ty_u = Mutate(node->ty, inplace_mode).as_or_throw<UnchangedOr<Type>>();
   }
-  UnchangedOr<Expr> op = ffi::Unchanged();
+  UnchangedOr<Expr> op_u = ffi::Unchanged();
   if (!node->op.as<OpNode>()) {
-    auto mapped_op = Mutate(node->op, inplace_mode);
-    op = std::move(mapped_op);
+    op_u = Mutate(node->op, inplace_mode);
   }
-  auto args = this->Mutate(node->args, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
-  UnchangedOr<ffi::Array<Type>> ty_args = ffi::Unchanged();
+  auto args_u = Mutate(node->args, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Expr>>>();
+  UnchangedOr<ffi::Array<Type>> ty_args_u = ffi::Unchanged();
   if (!node->ty_args.empty()) {
-    auto mapped_ty_args =
-        this->Mutate(node->ty_args, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Type>>>();
-    ty_args = std::move(mapped_ty_args);
+    ty_args_u = Mutate(node->ty_args, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<Type>>>();
   }
-  if (ty.UnchangedOrSameAs(node->ty) && op.UnchangedOrSameAs(node->op) &&
-      args.UnchangedOrSameAs(node->args) && 
ty_args.UnchangedOrSameAs(node->ty_args))
+  if (ty_u.UnchangedOrSameAs(node->ty) && op_u.UnchangedOrSameAs(node->op) &&
+      args_u.UnchangedOrSameAs(node->args) && 
ty_args_u.UnchangedOrSameAs(node->ty_args))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<CallNode*>(node);
-    if (!ty.IsUnchanged()) writable->ty = std::move(ty).ValueUnchecked();
-    if (!op.IsUnchanged()) writable->op = std::move(op).ValueUnchecked();
-    if (!args.IsUnchanged()) writable->args = std::move(args).ValueUnchecked();
-    if (!ty_args.IsUnchanged()) writable->ty_args = 
std::move(ty_args).ValueUnchecked();
+    if (!ty_u.IsUnchanged()) writable->ty = std::move(ty_u).ValueUnchecked();
+    if (!op_u.IsUnchanged()) writable->op = std::move(op_u).ValueUnchecked();
+    if (!args_u.IsUnchanged()) writable->args = 
std::move(args_u).ValueUnchecked();
+    if (!ty_args_u.IsUnchanged()) writable->ty_args = 
std::move(ty_args_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<CallNode>(*node);
-    copy->ty = std::move(ty).ValueOrUnchanged(std::move(copy->ty));
-    copy->op = std::move(op).ValueOrUnchanged(std::move(copy->op));
-    copy->args = std::move(args).ValueOrUnchanged(std::move(copy->args));
-    copy->ty_args = 
std::move(ty_args).ValueOrUnchanged(std::move(copy->ty_args));
-    return Expr(std::move(copy));
   }
+  auto copy = ffi::make_object<CallNode>(*node);
+  if (!ty_u.IsUnchanged()) copy->ty = std::move(ty_u).ValueUnchecked();
+  if (!op_u.IsUnchanged()) copy->op = std::move(op_u).ValueUnchecked();
+  if (!args_u.IsUnchanged()) copy->args = std::move(args_u).ValueUnchecked();
+  if (!ty_args_u.IsUnchanged()) copy->ty_args = 
std::move(ty_args_u).ValueUnchecked();
+  return Expr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const IntImmNode* node, InplaceMode 
inplace_mode) {
@@ -453,36 +443,34 @@ UnchangedOr<PrimExpr> ExprMutator::Mutate_(const 
prim::StringImmNode* node,
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::CastNode* node, 
InplaceMode inplace_mode) {
-  auto value = Mutate(node->value, inplace_mode);
-  if (value.UnchangedOrSameAs(node->value)) return ffi::Unchanged();
+  auto value_u = Mutate(node->value, inplace_mode);
+  if (value_u.UnchangedOrSameAs(node->value)) return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::CastNode*>(node);
-    if (!value.IsUnchanged()) writable->value = 
std::move(value).ValueUnchecked();
+    if (!value_u.IsUnchanged()) writable->value = 
std::move(value_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::CastNode>(*node);
-    copy->value = std::move(value).ValueOrUnchanged(std::move(copy->value));
-    return PrimExpr(std::move(copy));
   }
-}
-
-#define TVM_IR_BINARY_MUTATE_IMPL(Name)                                        
                \
-  UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::Name##Node* node,     
                \
-                                             InplaceMode inplace_mode) {       
                \
-    auto a = Mutate(node->a, inplace_mode);                                    
                \
-    auto b = Mutate(node->b, inplace_mode);                                    
                \
-    if (a.UnchangedOrSameAs(node->a) && b.UnchangedOrSameAs(node->b)) return 
ffi::Unchanged(); \
-    if (inplace_mode == InplaceMode::kAllow) {                                 
                \
-      auto* writable = const_cast<prim::Name##Node*>(node);                    
                \
-      if (!a.IsUnchanged()) writable->a = std::move(a).ValueUnchecked();       
                \
-      if (!b.IsUnchanged()) writable->b = std::move(b).ValueUnchecked();       
                \
-      return ffi::Unchanged();                                                 
                \
-    } else {                                                                   
                \
-      auto copy = ffi::make_object<prim::Name##Node>(*node);                   
                \
-      copy->a = std::move(a).ValueOrUnchanged(std::move(copy->a));             
                \
-      copy->b = std::move(b).ValueOrUnchanged(std::move(copy->b));             
                \
-      return PrimExpr(std::move(copy));                                        
                \
-    }                                                                          
                \
+  auto copy = ffi::make_object<prim::CastNode>(*node);
+  if (!value_u.IsUnchanged()) copy->value = 
std::move(value_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
+}
+
+#define TVM_IR_BINARY_MUTATE_IMPL(Name)                                        
                    \
+  UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::Name##Node* node,     
                    \
+                                             InplaceMode inplace_mode) {       
                    \
+    auto a_u = Mutate(node->a, inplace_mode);                                  
                    \
+    auto b_u = Mutate(node->b, inplace_mode);                                  
                    \
+    if (a_u.UnchangedOrSameAs(node->a) && b_u.UnchangedOrSameAs(node->b)) 
return ffi::Unchanged(); \
+    if (inplace_mode == InplaceMode::kAllow) {                                 
                    \
+      auto* writable = const_cast<prim::Name##Node*>(node);                    
                    \
+      if (!a_u.IsUnchanged()) writable->a = std::move(a_u).ValueUnchecked();   
                    \
+      if (!b_u.IsUnchanged()) writable->b = std::move(b_u).ValueUnchecked();   
                    \
+      return ffi::Unchanged();                                                 
                    \
+    }                                                                          
                    \
+    auto copy = ffi::make_object<prim::Name##Node>(*node);                     
                    \
+    if (!a_u.IsUnchanged()) copy->a = std::move(a_u).ValueUnchecked();         
                    \
+    if (!b_u.IsUnchanged()) copy->b = std::move(b_u).ValueUnchecked();         
                    \
+    return PrimExpr(std::move(copy));                                          
                    \
   }
 TVM_IR_BINARY_MUTATE_IMPL(Add)
 TVM_IR_BINARY_MUTATE_IMPL(Sub)
@@ -504,126 +492,122 @@ TVM_IR_BINARY_MUTATE_IMPL(Or)
 #undef TVM_IR_BINARY_MUTATE_IMPL
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::NotNode* node, 
InplaceMode inplace_mode) {
-  auto a = Mutate(node->a, inplace_mode);
-  if (a.UnchangedOrSameAs(node->a)) return ffi::Unchanged();
+  auto a_u = Mutate(node->a, inplace_mode);
+  if (a_u.UnchangedOrSameAs(node->a)) return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::NotNode*>(node);
-    if (!a.IsUnchanged()) writable->a = std::move(a).ValueUnchecked();
+    if (!a_u.IsUnchanged()) writable->a = std::move(a_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::NotNode>(*node);
-    copy->a = std::move(a).ValueOrUnchanged(std::move(copy->a));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<prim::NotNode>(*node);
+  if (!a_u.IsUnchanged()) copy->a = std::move(a_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::SelectNode* node, 
InplaceMode inplace_mode) {
-  auto condition = Mutate(node->condition, inplace_mode);
-  auto true_value = Mutate(node->true_value, inplace_mode);
-  auto false_value = Mutate(node->false_value, inplace_mode);
-  if (condition.UnchangedOrSameAs(node->condition) &&
-      true_value.UnchangedOrSameAs(node->true_value) &&
-      false_value.UnchangedOrSameAs(node->false_value))
+  auto condition_u = Mutate(node->condition, inplace_mode);
+  auto true_value_u = Mutate(node->true_value, inplace_mode);
+  auto false_value_u = Mutate(node->false_value, inplace_mode);
+  if (condition_u.UnchangedOrSameAs(node->condition) &&
+      true_value_u.UnchangedOrSameAs(node->true_value) &&
+      false_value_u.UnchangedOrSameAs(node->false_value))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::SelectNode*>(node);
-    if (!condition.IsUnchanged()) writable->condition = 
std::move(condition).ValueUnchecked();
-    if (!true_value.IsUnchanged()) writable->true_value = 
std::move(true_value).ValueUnchecked();
-    if (!false_value.IsUnchanged()) writable->false_value = 
std::move(false_value).ValueUnchecked();
+    if (!condition_u.IsUnchanged()) writable->condition = 
std::move(condition_u).ValueUnchecked();
+    if (!true_value_u.IsUnchanged())
+      writable->true_value = std::move(true_value_u).ValueUnchecked();
+    if (!false_value_u.IsUnchanged())
+      writable->false_value = std::move(false_value_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::SelectNode>(*node);
-    copy->condition = 
std::move(condition).ValueOrUnchanged(std::move(copy->condition));
-    copy->true_value = 
std::move(true_value).ValueOrUnchanged(std::move(copy->true_value));
-    copy->false_value = 
std::move(false_value).ValueOrUnchanged(std::move(copy->false_value));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<prim::SelectNode>(*node);
+  if (!condition_u.IsUnchanged()) copy->condition = 
std::move(condition_u).ValueUnchecked();
+  if (!true_value_u.IsUnchanged()) copy->true_value = 
std::move(true_value_u).ValueUnchecked();
+  if (!false_value_u.IsUnchanged()) copy->false_value = 
std::move(false_value_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::LetNode* node, 
InplaceMode inplace_mode) {
-  auto var = WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&] {
-               return Mutate(node->var, inplace_mode);
-             }).as_or_throw<UnchangedOr<Var>>();
-  auto value = Mutate(node->value, inplace_mode);
-  auto body = Mutate(node->body, inplace_mode);
-  if (var.UnchangedOrSameAs(node->var) && value.UnchangedOrSameAs(node->value) 
&&
-      body.UnchangedOrSameAs(node->body))
+  auto var_u = WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&] {
+                 return Mutate(node->var, inplace_mode);
+               }).as_or_throw<UnchangedOr<Var>>();
+  auto value_u = Mutate(node->value, inplace_mode);
+  auto body_u = Mutate(node->body, inplace_mode);
+  if (var_u.UnchangedOrSameAs(node->var) && 
value_u.UnchangedOrSameAs(node->value) &&
+      body_u.UnchangedOrSameAs(node->body))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::LetNode*>(node);
-    if (!var.IsUnchanged()) writable->var = std::move(var).ValueUnchecked();
-    if (!value.IsUnchanged()) writable->value = 
std::move(value).ValueUnchecked();
-    if (!body.IsUnchanged()) writable->body = std::move(body).ValueUnchecked();
+    if (!var_u.IsUnchanged()) writable->var = 
std::move(var_u).ValueUnchecked();
+    if (!value_u.IsUnchanged()) writable->value = 
std::move(value_u).ValueUnchecked();
+    if (!body_u.IsUnchanged()) writable->body = 
std::move(body_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::LetNode>(*node);
-    copy->var = std::move(var).ValueOrUnchanged(std::move(copy->var));
-    copy->value = std::move(value).ValueOrUnchanged(std::move(copy->value));
-    copy->body = std::move(body).ValueOrUnchanged(std::move(copy->body));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<prim::LetNode>(*node);
+  if (!var_u.IsUnchanged()) copy->var = std::move(var_u).ValueUnchecked();
+  if (!value_u.IsUnchanged()) copy->value = 
std::move(value_u).ValueUnchecked();
+  if (!body_u.IsUnchanged()) copy->body = std::move(body_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::RampNode* node, 
InplaceMode inplace_mode) {
-  auto base = Mutate(node->base, inplace_mode);
-  auto stride = Mutate(node->stride, inplace_mode);
-  auto lanes = Mutate(node->lanes, inplace_mode);
-  if (base.UnchangedOrSameAs(node->base) && 
stride.UnchangedOrSameAs(node->stride) &&
-      lanes.UnchangedOrSameAs(node->lanes))
+  auto base_u = Mutate(node->base, inplace_mode);
+  auto stride_u = Mutate(node->stride, inplace_mode);
+  auto lanes_u = Mutate(node->lanes, inplace_mode);
+  if (base_u.UnchangedOrSameAs(node->base) && 
stride_u.UnchangedOrSameAs(node->stride) &&
+      lanes_u.UnchangedOrSameAs(node->lanes))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::RampNode*>(node);
-    if (!base.IsUnchanged()) writable->base = std::move(base).ValueUnchecked();
-    if (!stride.IsUnchanged()) writable->stride = 
std::move(stride).ValueUnchecked();
-    if (!lanes.IsUnchanged()) writable->lanes = 
std::move(lanes).ValueUnchecked();
+    if (!base_u.IsUnchanged()) writable->base = 
std::move(base_u).ValueUnchecked();
+    if (!stride_u.IsUnchanged()) writable->stride = 
std::move(stride_u).ValueUnchecked();
+    if (!lanes_u.IsUnchanged()) writable->lanes = 
std::move(lanes_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::RampNode>(*node);
-    copy->base = std::move(base).ValueOrUnchanged(std::move(copy->base));
-    copy->stride = std::move(stride).ValueOrUnchanged(std::move(copy->stride));
-    copy->lanes = std::move(lanes).ValueOrUnchanged(std::move(copy->lanes));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<prim::RampNode>(*node);
+  if (!base_u.IsUnchanged()) copy->base = std::move(base_u).ValueUnchecked();
+  if (!stride_u.IsUnchanged()) copy->stride = 
std::move(stride_u).ValueUnchecked();
+  if (!lanes_u.IsUnchanged()) copy->lanes = 
std::move(lanes_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::BroadcastNode* node,
                                            InplaceMode inplace_mode) {
-  auto value = Mutate(node->value, inplace_mode);
-  auto lanes = Mutate(node->lanes, inplace_mode);
-  if (value.UnchangedOrSameAs(node->value) && 
lanes.UnchangedOrSameAs(node->lanes))
+  auto value_u = Mutate(node->value, inplace_mode);
+  auto lanes_u = Mutate(node->lanes, inplace_mode);
+  if (value_u.UnchangedOrSameAs(node->value) && 
lanes_u.UnchangedOrSameAs(node->lanes))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::BroadcastNode*>(node);
-    if (!value.IsUnchanged()) writable->value = 
std::move(value).ValueUnchecked();
-    if (!lanes.IsUnchanged()) writable->lanes = 
std::move(lanes).ValueUnchecked();
+    if (!value_u.IsUnchanged()) writable->value = 
std::move(value_u).ValueUnchecked();
+    if (!lanes_u.IsUnchanged()) writable->lanes = 
std::move(lanes_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::BroadcastNode>(*node);
-    copy->value = std::move(value).ValueOrUnchanged(std::move(copy->value));
-    copy->lanes = std::move(lanes).ValueOrUnchanged(std::move(copy->lanes));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<prim::BroadcastNode>(*node);
+  if (!value_u.IsUnchanged()) copy->value = 
std::move(value_u).ValueUnchecked();
+  if (!lanes_u.IsUnchanged()) copy->lanes = 
std::move(lanes_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<PrimExpr> ExprMutator::Mutate_(const prim::ShuffleNode* node,
                                            InplaceMode inplace_mode) {
-  auto vectors =
-      this->Mutate(node->vectors, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
-  auto indices =
-      this->Mutate(node->indices, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
-  if (vectors.UnchangedOrSameAs(node->vectors) && 
indices.UnchangedOrSameAs(node->indices))
+  auto vectors_u =
+      Mutate(node->vectors, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
+  auto indices_u =
+      Mutate(node->indices, 
inplace_mode).as_or_throw<UnchangedOr<ffi::Array<PrimExpr>>>();
+  if (vectors_u.UnchangedOrSameAs(node->vectors) && 
indices_u.UnchangedOrSameAs(node->indices))
     return ffi::Unchanged();
   if (inplace_mode == InplaceMode::kAllow) {
     auto* writable = const_cast<prim::ShuffleNode*>(node);
-    if (!vectors.IsUnchanged()) writable->vectors = 
std::move(vectors).ValueUnchecked();
-    if (!indices.IsUnchanged()) writable->indices = 
std::move(indices).ValueUnchecked();
+    if (!vectors_u.IsUnchanged()) writable->vectors = 
std::move(vectors_u).ValueUnchecked();
+    if (!indices_u.IsUnchanged()) writable->indices = 
std::move(indices_u).ValueUnchecked();
     return ffi::Unchanged();
-  } else {
-    auto copy = ffi::make_object<prim::ShuffleNode>(*node);
-    copy->vectors = 
std::move(vectors).ValueOrUnchanged(std::move(copy->vectors));
-    copy->indices = 
std::move(indices).ValueOrUnchanged(std::move(copy->indices));
-    return PrimExpr(std::move(copy));
   }
+  auto copy = ffi::make_object<prim::ShuffleNode>(*node);
+  if (!vectors_u.IsUnchanged()) copy->vectors = 
std::move(vectors_u).ValueUnchecked();
+  if (!indices_u.IsUnchanged()) copy->indices = 
std::move(indices_u).ValueUnchecked();
+  return PrimExpr(std::move(copy));
 }
 
 UnchangedOr<Expr> ExprMutator::Mutate_(const VarNode* node, InplaceMode 
inplace_mode) {
@@ -636,34 +620,34 @@ UnchangedOr<Expr> ExprMutator::Mutate_(const VarNode* 
node, InplaceMode inplace_
     return std::move(remap_result).as_or_throw<UnchangedOr<Expr>>();
   }
   if (def_region_kind() == kTVMFFIDefRegionKindNone) return ffi::Unchanged();
-  UnchangedOr<Expr> result = ffi::Unchanged();
+  UnchangedOr<Expr> result_u = ffi::Unchanged();
   ffi::Any mapped_value = ffi::Unchanged();
   // PrimType has no children; dynamic type fields inherit Pattern but are 
visited outside Simple.
   if (!node->ty.as<PrimTypeNode>()) {
-    UnchangedOr<ffi::Any> mapped_ty_result =
+    UnchangedOr<ffi::Any> mapped_ty_result_u =
         def_region_kind() == kTVMFFIDefRegionKindSimple
             ? WithDefRegionKind(kTVMFFIDefRegionKindNone,
-                                [&] { return this->Mutate(node->ty, 
inplace_mode); })
-            : this->Mutate(node->ty, inplace_mode);
-    auto mapped_ty = 
std::move(mapped_ty_result).as_or_throw<UnchangedOr<Type>>();
-    if (!mapped_ty.UnchangedOrSameAs(node->ty)) {
+                                [&] { return Mutate(node->ty, inplace_mode); })
+            : Mutate(node->ty, inplace_mode);
+    auto mapped_ty_u = 
std::move(mapped_ty_result_u).as_or_throw<UnchangedOr<Type>>();
+    if (!mapped_ty_u.UnchangedOrSameAs(node->ty)) {
       Expr mapped_expr;
       if (inplace_mode == InplaceMode::kAllow) {
-        const_cast<VarNode*>(node)->ty = std::move(mapped_ty).ValueUnchecked();
+        const_cast<VarNode*>(node)->ty = 
std::move(mapped_ty_u).ValueUnchecked();
         mapped_expr = ffi::GetRef<Expr>(node);
       } else {
         auto copy = ffi::make_object<VarNode>(*node);
-        copy->ty = std::move(mapped_ty).ValueUnchecked();
+        copy->ty = std::move(mapped_ty_u).ValueUnchecked();
         mapped_expr = Expr(std::move(copy));
       }
-      result = mapped_expr;
+      result_u = mapped_expr;
       mapped_value = std::move(mapped_expr);
     }
   }
-  if (!result.IsUnchanged() || def_region_kind() == 
kTVMFFIDefRegionKindPattern) {
+  if (!result_u.IsUnchanged() || def_region_kind() == 
kTVMFFIDefRegionKindPattern) {
     VarRemapSet(ffi::AnyView(node), mapped_value);
   }
-  return result;
+  return result_u;
 }
 
 }  // namespace tvm
diff --git a/src/ir/prim/expr.cc b/src/ir/prim/expr.cc
index 0ca93446cd..48de984613 100644
--- a/src/ir/prim/expr.cc
+++ b/src/ir/prim/expr.cc
@@ -63,19 +63,19 @@ TVMFFIAny BinaryMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) n
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const TNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, a,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, a_u,
                                     mutator->MutateExpected(self->a));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, b,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, b_u,
                                     mutator->MutateExpected(self->b));
-  if (a.UnchangedOrSameAs(self->a) && b.UnchangedOrSameAs(self->b)) {
+  if (a_u.UnchangedOrSameAs(self->a) && b_u.UnchangedOrSameAs(self->b)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<TNode> copy = ffi::make_object<TNode>(*self);
   // StructuralMap preserves node types. A rewrite that changes operand dtypes 
must keep the
   // operands compatible and set the result type itself; a generic traversal 
cannot infer the
   // casts that would require.
-  copy->a = std::move(a).ValueOrUnchanged(std::move(copy->a));
-  copy->b = std::move(b).ValueOrUnchanged(std::move(copy->b));
+  if (!a_u.IsUnchanged()) copy->a = std::move(a_u).ValueUnchecked();
+  if (!b_u.IsUnchanged()) copy->b = std::move(b_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -85,15 +85,15 @@ TVMFFIAny 
BinaryMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   TNode* self = const_cast<TNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, a,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, a_u,
                                     mutator->MutateExpected(self->a, 
ffi::InplaceMode::kAllow));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, b,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, b_u,
                                     mutator->MutateExpected(self->b, 
ffi::InplaceMode::kAllow));
-  if (a.UnchangedOrSameAs(self->a) && b.UnchangedOrSameAs(self->b)) {
+  if (a_u.UnchangedOrSameAs(self->a) && b_u.UnchangedOrSameAs(self->b)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!a.IsUnchanged()) self->a = std::move(a).ValueUnchecked();
-  if (!b.IsUnchanged()) self->b = std::move(b).ValueUnchecked();
+  if (!a_u.IsUnchanged()) self->a = std::move(a_u).ValueUnchecked();
+  if (!b_u.IsUnchanged()) self->b = std::move(b_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -130,13 +130,13 @@ TVMFFIAny CastMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noe
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const CastNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
CastNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value_u,
                                     mutator->MutateExpected(self->value));
-  if (mapped_value.UnchangedOrSameAs(self->value)) {
+  if (mapped_value_u.UnchangedOrSameAs(self->value)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<CastNode> copy = ffi::make_object<CastNode>(*self);
-  copy->value = 
std::move(mapped_value).ValueOrUnchanged(std::move(copy->value));
+  if (!mapped_value_u.IsUnchanged()) copy->value = 
std::move(mapped_value_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -144,12 +144,12 @@ TVMFFIAny 
CastMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyVie
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   CastNode* self = const_cast<CastNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
CastNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value_u,
                                     mutator->MutateExpected(self->value, 
ffi::InplaceMode::kAllow));
-  if (mapped_value.UnchangedOrSameAs(self->value)) {
+  if (mapped_value_u.UnchangedOrSameAs(self->value)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_value.IsUnchanged()) self->value = 
std::move(mapped_value).ValueUnchecked();
+  if (!mapped_value_u.IsUnchanged()) self->value = 
std::move(mapped_value_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -165,13 +165,13 @@ TVMFFIAny NotMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noex
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const NotNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
NotNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_a,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_a_u,
                                     mutator->MutateExpected(self->a));
-  if (mapped_a.UnchangedOrSameAs(self->a)) {
+  if (mapped_a_u.UnchangedOrSameAs(self->a)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<NotNode> copy = ffi::make_object<NotNode>(*self);
-  copy->a = std::move(mapped_a).ValueOrUnchanged(std::move(copy->a));
+  if (!mapped_a_u.IsUnchanged()) copy->a = 
std::move(mapped_a_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -179,12 +179,12 @@ TVMFFIAny 
NotMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   NotNode* self = const_cast<NotNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
NotNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_a,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_a_u,
                                     mutator->MutateExpected(self->a, 
ffi::InplaceMode::kAllow));
-  if (mapped_a.UnchangedOrSameAs(self->a)) {
+  if (mapped_a_u.UnchangedOrSameAs(self->a)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_a.IsUnchanged()) self->a = std::move(mapped_a).ValueUnchecked();
+  if (!mapped_a_u.IsUnchanged()) self->a = 
std::move(mapped_a_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -202,21 +202,24 @@ TVMFFIAny SelectMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyView value) n
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const SelectNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
SelectNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_condition,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_condition_u,
                                     mutator->MutateExpected(self->condition));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_true_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_true_value_u,
                                     mutator->MutateExpected(self->true_value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_false_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_false_value_u,
                                     
mutator->MutateExpected(self->false_value));
-  if (mapped_condition.UnchangedOrSameAs(self->condition) &&
-      mapped_true_value.UnchangedOrSameAs(self->true_value) &&
-      mapped_false_value.UnchangedOrSameAs(self->false_value)) {
+  if (mapped_condition_u.UnchangedOrSameAs(self->condition) &&
+      mapped_true_value_u.UnchangedOrSameAs(self->true_value) &&
+      mapped_false_value_u.UnchangedOrSameAs(self->false_value)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<SelectNode> copy = ffi::make_object<SelectNode>(*self);
-  copy->condition = 
std::move(mapped_condition).ValueOrUnchanged(std::move(copy->condition));
-  copy->true_value = 
std::move(mapped_true_value).ValueOrUnchanged(std::move(copy->true_value));
-  copy->false_value = 
std::move(mapped_false_value).ValueOrUnchanged(std::move(copy->false_value));
+  if (!mapped_condition_u.IsUnchanged())
+    copy->condition = std::move(mapped_condition_u).ValueUnchecked();
+  if (!mapped_true_value_u.IsUnchanged())
+    copy->true_value = std::move(mapped_true_value_u).ValueUnchecked();
+  if (!mapped_false_value_u.IsUnchanged())
+    copy->false_value = std::move(mapped_false_value_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -226,25 +229,25 @@ TVMFFIAny 
SelectMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   SelectNode* self = const_cast<SelectNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
SelectNode>(value));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<PrimExpr>, mapped_condition,
+      ffi::UnchangedOr<PrimExpr>, mapped_condition_u,
       mutator->MutateExpected(self->condition, ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<PrimExpr>, mapped_true_value,
+      ffi::UnchangedOr<PrimExpr>, mapped_true_value_u,
       mutator->MutateExpected(self->true_value, ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<PrimExpr>, mapped_false_value,
+      ffi::UnchangedOr<PrimExpr>, mapped_false_value_u,
       mutator->MutateExpected(self->false_value, ffi::InplaceMode::kAllow));
-  if (mapped_condition.UnchangedOrSameAs(self->condition) &&
-      mapped_true_value.UnchangedOrSameAs(self->true_value) &&
-      mapped_false_value.UnchangedOrSameAs(self->false_value)) {
+  if (mapped_condition_u.UnchangedOrSameAs(self->condition) &&
+      mapped_true_value_u.UnchangedOrSameAs(self->true_value) &&
+      mapped_false_value_u.UnchangedOrSameAs(self->false_value)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_condition.IsUnchanged())
-    self->condition = std::move(mapped_condition).ValueUnchecked();
-  if (!mapped_true_value.IsUnchanged())
-    self->true_value = std::move(mapped_true_value).ValueUnchecked();
-  if (!mapped_false_value.IsUnchanged())
-    self->false_value = std::move(mapped_false_value).ValueUnchecked();
+  if (!mapped_condition_u.IsUnchanged())
+    self->condition = std::move(mapped_condition_u).ValueUnchecked();
+  if (!mapped_true_value_u.IsUnchanged())
+    self->true_value = std::move(mapped_true_value_u).ValueUnchecked();
+  if (!mapped_false_value_u.IsUnchanged())
+    self->false_value = std::move(mapped_false_value_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -263,22 +266,22 @@ TVMFFIAny LetMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noex
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const LetNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
LetNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Var>, mapped_var,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Var>, mapped_var_u,
                                     
mutator->WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&]() {
                                       return 
mutator->MutateExpected(self->var);
                                     }));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value_u,
                                     mutator->MutateExpected(self->value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_body,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_body_u,
                                     mutator->MutateExpected(self->body));
-  if (mapped_var.UnchangedOrSameAs(self->var) && 
mapped_value.UnchangedOrSameAs(self->value) &&
-      mapped_body.UnchangedOrSameAs(self->body)) {
+  if (mapped_var_u.UnchangedOrSameAs(self->var) && 
mapped_value_u.UnchangedOrSameAs(self->value) &&
+      mapped_body_u.UnchangedOrSameAs(self->body)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<LetNode> copy = ffi::make_object<LetNode>(*self);
-  copy->var = std::move(mapped_var).ValueOrUnchanged(std::move(copy->var));
-  copy->value = 
std::move(mapped_value).ValueOrUnchanged(std::move(copy->value));
-  copy->body = std::move(mapped_body).ValueOrUnchanged(std::move(copy->body));
+  if (!mapped_var_u.IsUnchanged()) copy->var = 
std::move(mapped_var_u).ValueUnchecked();
+  if (!mapped_value_u.IsUnchanged()) copy->value = 
std::move(mapped_value_u).ValueUnchecked();
+  if (!mapped_body_u.IsUnchanged()) copy->body = 
std::move(mapped_body_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -286,22 +289,22 @@ TVMFFIAny 
LetMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   LetNode* self = const_cast<LetNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
LetNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Var>, mapped_var,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Var>, mapped_var_u,
                                     
mutator->WithDefRegionKind(kTVMFFIDefRegionKindSimple, [&]() {
                                       return mutator->MutateExpected(self->var,
                                                                      
ffi::InplaceMode::kAllow);
                                     }));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value_u,
                                     mutator->MutateExpected(self->value, 
ffi::InplaceMode::kAllow));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_body,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_body_u,
                                     mutator->MutateExpected(self->body, 
ffi::InplaceMode::kAllow));
-  if (mapped_var.UnchangedOrSameAs(self->var) && 
mapped_value.UnchangedOrSameAs(self->value) &&
-      mapped_body.UnchangedOrSameAs(self->body)) {
+  if (mapped_var_u.UnchangedOrSameAs(self->var) && 
mapped_value_u.UnchangedOrSameAs(self->value) &&
+      mapped_body_u.UnchangedOrSameAs(self->body)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_var.IsUnchanged()) self->var = 
std::move(mapped_var).ValueUnchecked();
-  if (!mapped_value.IsUnchanged()) self->value = 
std::move(mapped_value).ValueUnchecked();
-  if (!mapped_body.IsUnchanged()) self->body = 
std::move(mapped_body).ValueUnchecked();
+  if (!mapped_var_u.IsUnchanged()) self->var = 
std::move(mapped_var_u).ValueUnchecked();
+  if (!mapped_value_u.IsUnchanged()) self->value = 
std::move(mapped_value_u).ValueUnchecked();
+  if (!mapped_body_u.IsUnchanged()) self->body = 
std::move(mapped_body_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
diff --git a/src/ir/prim/vector_expr.cc b/src/ir/prim/vector_expr.cc
index acf3f04245..5a4a939bc6 100644
--- a/src/ir/prim/vector_expr.cc
+++ b/src/ir/prim/vector_expr.cc
@@ -67,20 +67,21 @@ TVMFFIAny RampMutate(ffi::StructuralMutatorObj* mutator, 
ffi::AnyView value) noe
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const RampNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
RampNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_base,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_base_u,
                                     mutator->MutateExpected(self->base));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_stride,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, 
mapped_stride_u,
                                     mutator->MutateExpected(self->stride));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes_u,
                                     mutator->MutateExpected(self->lanes));
-  if (mapped_base.UnchangedOrSameAs(self->base) && 
mapped_stride.UnchangedOrSameAs(self->stride) &&
-      mapped_lanes.UnchangedOrSameAs(self->lanes)) {
+  if (mapped_base_u.UnchangedOrSameAs(self->base) &&
+      mapped_stride_u.UnchangedOrSameAs(self->stride) &&
+      mapped_lanes_u.UnchangedOrSameAs(self->lanes)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<RampNode> copy = ffi::make_object<RampNode>(*self);
-  copy->base = std::move(mapped_base).ValueOrUnchanged(std::move(copy->base));
-  copy->stride = 
std::move(mapped_stride).ValueOrUnchanged(std::move(copy->stride));
-  copy->lanes = 
std::move(mapped_lanes).ValueOrUnchanged(std::move(copy->lanes));
+  if (!mapped_base_u.IsUnchanged()) copy->base = 
std::move(mapped_base_u).ValueUnchecked();
+  if (!mapped_stride_u.IsUnchanged()) copy->stride = 
std::move(mapped_stride_u).ValueUnchecked();
+  if (!mapped_lanes_u.IsUnchanged()) copy->lanes = 
std::move(mapped_lanes_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -88,20 +89,21 @@ TVMFFIAny RampMaybeInplaceMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyVie
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   RampNode* self = const_cast<RampNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
RampNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_base,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_base_u,
                                     mutator->MutateExpected(self->base, 
ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<PrimExpr>, mapped_stride,
+      ffi::UnchangedOr<PrimExpr>, mapped_stride_u,
       mutator->MutateExpected(self->stride, ffi::InplaceMode::kAllow));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes_u,
                                     mutator->MutateExpected(self->lanes, 
ffi::InplaceMode::kAllow));
-  if (mapped_base.UnchangedOrSameAs(self->base) && 
mapped_stride.UnchangedOrSameAs(self->stride) &&
-      mapped_lanes.UnchangedOrSameAs(self->lanes)) {
+  if (mapped_base_u.UnchangedOrSameAs(self->base) &&
+      mapped_stride_u.UnchangedOrSameAs(self->stride) &&
+      mapped_lanes_u.UnchangedOrSameAs(self->lanes)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_base.IsUnchanged()) self->base = 
std::move(mapped_base).ValueUnchecked();
-  if (!mapped_stride.IsUnchanged()) self->stride = 
std::move(mapped_stride).ValueUnchecked();
-  if (!mapped_lanes.IsUnchanged()) self->lanes = 
std::move(mapped_lanes).ValueUnchecked();
+  if (!mapped_base_u.IsUnchanged()) self->base = 
std::move(mapped_base_u).ValueUnchecked();
+  if (!mapped_stride_u.IsUnchanged()) self->stride = 
std::move(mapped_stride_u).ValueUnchecked();
+  if (!mapped_lanes_u.IsUnchanged()) self->lanes = 
std::move(mapped_lanes_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -118,16 +120,17 @@ TVMFFIAny BroadcastMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyView value
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const BroadcastNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
BroadcastNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value_u,
                                     mutator->MutateExpected(self->value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes_u,
                                     mutator->MutateExpected(self->lanes));
-  if (mapped_value.UnchangedOrSameAs(self->value) && 
mapped_lanes.UnchangedOrSameAs(self->lanes)) {
+  if (mapped_value_u.UnchangedOrSameAs(self->value) &&
+      mapped_lanes_u.UnchangedOrSameAs(self->lanes)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<BroadcastNode> copy = ffi::make_object<BroadcastNode>(*self);
-  copy->value = 
std::move(mapped_value).ValueOrUnchanged(std::move(copy->value));
-  copy->lanes = 
std::move(mapped_lanes).ValueOrUnchanged(std::move(copy->lanes));
+  if (!mapped_value_u.IsUnchanged()) copy->value = 
std::move(mapped_value_u).ValueUnchecked();
+  if (!mapped_lanes_u.IsUnchanged()) copy->lanes = 
std::move(mapped_lanes_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -136,15 +139,16 @@ TVMFFIAny 
BroadcastMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   BroadcastNode* self = const_cast<BroadcastNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
BroadcastNode>(value));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_value_u,
                                     mutator->MutateExpected(self->value, 
ffi::InplaceMode::kAllow));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<PrimExpr>, mapped_lanes_u,
                                     mutator->MutateExpected(self->lanes, 
ffi::InplaceMode::kAllow));
-  if (mapped_value.UnchangedOrSameAs(self->value) && 
mapped_lanes.UnchangedOrSameAs(self->lanes)) {
+  if (mapped_value_u.UnchangedOrSameAs(self->value) &&
+      mapped_lanes_u.UnchangedOrSameAs(self->lanes)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_value.IsUnchanged()) self->value = 
std::move(mapped_value).ValueUnchecked();
-  if (!mapped_lanes.IsUnchanged()) self->lanes = 
std::move(mapped_lanes).ValueUnchecked();
+  if (!mapped_value_u.IsUnchanged()) self->value = 
std::move(mapped_value_u).ValueUnchecked();
+  if (!mapped_lanes_u.IsUnchanged()) self->lanes = 
std::move(mapped_lanes_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
@@ -161,17 +165,17 @@ TVMFFIAny ShuffleMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyView value)
   // skips: PrimExpr types are always PrimType and remain unchanged in normal 
mutation.
   const ShuffleNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
ShuffleNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<PrimExpr>>, 
mapped_vectors,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<PrimExpr>>, 
mapped_vectors_u,
                                     mutator->MutateExpected(self->vectors));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<PrimExpr>>, 
mapped_indices,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<PrimExpr>>, 
mapped_indices_u,
                                     mutator->MutateExpected(self->indices));
-  if (mapped_vectors.UnchangedOrSameAs(self->vectors) &&
-      mapped_indices.UnchangedOrSameAs(self->indices)) {
+  if (mapped_vectors_u.UnchangedOrSameAs(self->vectors) &&
+      mapped_indices_u.UnchangedOrSameAs(self->indices)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<ShuffleNode> copy = ffi::make_object<ShuffleNode>(*self);
-  copy->vectors = 
std::move(mapped_vectors).ValueOrUnchanged(std::move(copy->vectors));
-  copy->indices = 
std::move(mapped_indices).ValueOrUnchanged(std::move(copy->indices));
+  if (!mapped_vectors_u.IsUnchanged()) copy->vectors = 
std::move(mapped_vectors_u).ValueUnchecked();
+  if (!mapped_indices_u.IsUnchanged()) copy->indices = 
std::move(mapped_indices_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -181,17 +185,17 @@ TVMFFIAny 
ShuffleMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   ShuffleNode* self = const_cast<ShuffleNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
ShuffleNode>(value));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<ffi::Array<PrimExpr>>, mapped_vectors,
+      ffi::UnchangedOr<ffi::Array<PrimExpr>>, mapped_vectors_u,
       mutator->MutateExpected(self->vectors, ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<ffi::Array<PrimExpr>>, mapped_indices,
+      ffi::UnchangedOr<ffi::Array<PrimExpr>>, mapped_indices_u,
       mutator->MutateExpected(self->indices, ffi::InplaceMode::kAllow));
-  if (mapped_vectors.UnchangedOrSameAs(self->vectors) &&
-      mapped_indices.UnchangedOrSameAs(self->indices)) {
+  if (mapped_vectors_u.UnchangedOrSameAs(self->vectors) &&
+      mapped_indices_u.UnchangedOrSameAs(self->indices)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
-  if (!mapped_vectors.IsUnchanged()) self->vectors = 
std::move(mapped_vectors).ValueUnchecked();
-  if (!mapped_indices.IsUnchanged()) self->indices = 
std::move(mapped_indices).ValueUnchecked();
+  if (!mapped_vectors_u.IsUnchanged()) self->vectors = 
std::move(mapped_vectors_u).ValueUnchecked();
+  if (!mapped_indices_u.IsUnchanged()) self->indices = 
std::move(mapped_indices_u).ValueUnchecked();
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
 
diff --git a/src/ir/type.cc b/src/ir/type.cc
index 06e71c3b38..dd28b98e55 100644
--- a/src/ir/type.cc
+++ b/src/ir/type.cc
@@ -125,14 +125,14 @@ TVMFFIAny PointerTypeMutate(ffi::StructuralMutatorObj* 
mutator, ffi::AnyView val
   // skips: storage_scope (scalar)
   const PointerTypeNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
PointerTypeNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, 
mapped_element_type,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, 
mapped_element_type_u,
                                     
mutator->MutateExpected(self->element_type));
-  if (mapped_element_type.UnchangedOrSameAs(self->element_type)) {
+  if (mapped_element_type_u.UnchangedOrSameAs(self->element_type)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<PointerTypeNode> copy = 
ffi::make_object<PointerTypeNode>(*self);
-  copy->element_type =
-      
std::move(mapped_element_type).ValueOrUnchanged(std::move(copy->element_type));
+  if (!mapped_element_type_u.IsUnchanged())
+    copy->element_type = std::move(mapped_element_type_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -142,10 +142,10 @@ TVMFFIAny 
PointerTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   PointerTypeNode* self = const_cast<PointerTypeNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
PointerTypeNode>(value));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<Type>, mapped_element_type,
+      ffi::UnchangedOr<Type>, mapped_element_type_u,
       mutator->MutateExpected(self->element_type, ffi::InplaceMode::kAllow));
-  if (!mapped_element_type.UnchangedOrSameAs(self->element_type)) {
-    self->element_type = std::move(mapped_element_type).ValueUnchecked();
+  if (!mapped_element_type_u.UnchangedOrSameAs(self->element_type)) {
+    self->element_type = std::move(mapped_element_type_u).ValueUnchecked();
   }
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
@@ -161,17 +161,19 @@ TVMFFIAny FuncTypeVisit(ffi::StructuralVisitorObj* 
visitor, ffi::AnyView value)
 TVMFFIAny FuncTypeMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView 
value) noexcept {
   const FuncTypeNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
FuncTypeNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, 
mapped_arg_types,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, 
mapped_arg_types_u,
                                     mutator->MutateExpected(self->arg_types));
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ret_type,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<Type>, mapped_ret_type_u,
                                     mutator->MutateExpected(self->ret_type));
-  if (mapped_arg_types.UnchangedOrSameAs(self->arg_types) &&
-      mapped_ret_type.UnchangedOrSameAs(self->ret_type)) {
+  if (mapped_arg_types_u.UnchangedOrSameAs(self->arg_types) &&
+      mapped_ret_type_u.UnchangedOrSameAs(self->ret_type)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<FuncTypeNode> copy = ffi::make_object<FuncTypeNode>(*self);
-  copy->arg_types = 
std::move(mapped_arg_types).ValueOrUnchanged(std::move(copy->arg_types));
-  copy->ret_type = 
std::move(mapped_ret_type).ValueOrUnchanged(std::move(copy->ret_type));
+  if (!mapped_arg_types_u.IsUnchanged())
+    copy->arg_types = std::move(mapped_arg_types_u).ValueUnchecked();
+  if (!mapped_ret_type_u.IsUnchanged())
+    copy->ret_type = std::move(mapped_ret_type_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -180,16 +182,16 @@ TVMFFIAny 
FuncTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   FuncTypeNode* self = const_cast<FuncTypeNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
FuncTypeNode>(value));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<ffi::Array<Type>>, mapped_arg_types,
+      ffi::UnchangedOr<ffi::Array<Type>>, mapped_arg_types_u,
       mutator->MutateExpected(self->arg_types, ffi::InplaceMode::kAllow));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<Type>, mapped_ret_type,
+      ffi::UnchangedOr<Type>, mapped_ret_type_u,
       mutator->MutateExpected(self->ret_type, ffi::InplaceMode::kAllow));
-  if (!mapped_arg_types.UnchangedOrSameAs(self->arg_types)) {
-    self->arg_types = std::move(mapped_arg_types).ValueUnchecked();
+  if (!mapped_arg_types_u.UnchangedOrSameAs(self->arg_types)) {
+    self->arg_types = std::move(mapped_arg_types_u).ValueUnchecked();
   }
-  if (!mapped_ret_type.UnchangedOrSameAs(self->ret_type)) {
-    self->ret_type = std::move(mapped_ret_type).ValueUnchecked();
+  if (!mapped_ret_type_u.UnchangedOrSameAs(self->ret_type)) {
+    self->ret_type = std::move(mapped_ret_type_u).ValueUnchecked();
   }
   return ffi::Unchanged().CopyToTVMFFIAny();
 }
@@ -204,13 +206,13 @@ TVMFFIAny TupleTypeVisit(ffi::StructuralVisitorObj* 
visitor, ffi::AnyView value)
 TVMFFIAny TupleTypeMutate(ffi::StructuralMutatorObj* mutator, ffi::AnyView 
value) noexcept {
   const TupleTypeNode* self =
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TupleTypeNode>(value);
-  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, 
mapped_fields,
+  TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(ffi::UnchangedOr<ffi::Array<Type>>, 
mapped_fields_u,
                                     mutator->MutateExpected(self->fields));
-  if (mapped_fields.UnchangedOrSameAs(self->fields)) {
+  if (mapped_fields_u.UnchangedOrSameAs(self->fields)) {
     return ffi::Unchanged().CopyToTVMFFIAny();
   }
   ffi::ObjectPtr<TupleTypeNode> copy = ffi::make_object<TupleTypeNode>(*self);
-  copy->fields = 
std::move(mapped_fields).ValueOrUnchanged(std::move(copy->fields));
+  if (!mapped_fields_u.IsUnchanged()) copy->fields = 
std::move(mapped_fields_u).ValueUnchecked();
   return 
ffi::details::AnyUnsafe::MoveAnyToTVMFFIAny(ffi::Any(std::move(copy)));
 }
 
@@ -219,10 +221,10 @@ TVMFFIAny 
TupleTypeMaybeInplaceMutate(ffi::StructuralMutatorObj* mutator,
   TupleTypeNode* self = const_cast<TupleTypeNode*>(
       ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const 
TupleTypeNode>(value));
   TVM_FFI_S_MUTATE_ASSIGN_OR_RETURN(
-      ffi::UnchangedOr<ffi::Array<Type>>, mapped_fields,
+      ffi::UnchangedOr<ffi::Array<Type>>, mapped_fields_u,
       mutator->MutateExpected(self->fields, ffi::InplaceMode::kAllow));
-  if (!mapped_fields.UnchangedOrSameAs(self->fields)) {
-    self->fields = std::move(mapped_fields).ValueUnchecked();
+  if (!mapped_fields_u.UnchangedOrSameAs(self->fields)) {
+    self->fields = std::move(mapped_fields_u).ValueUnchecked();
   }
   return ffi::Unchanged().CopyToTVMFFIAny();
 }

Reply via email to