This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new 8682539f [REFACTOR][RUST] Clarify structural mutator dispatch and 
recursion APIs (#714)
8682539f is described below

commit 8682539fda858409ab4705cd4b02e726f453f8d2
Author: Shushi Hong <[email protected]>
AuthorDate: Sat Aug 29 14:40:12 2026 -0400

    [REFACTOR][RUST] Clarify structural mutator dispatch and recursion APIs 
(#714)
    
    This PR clarifies the Rust structural-mutation API by separating engine
    dispatch hooks from user-controlled recursive mutation.
    
    The engine-facing hooks are renamed to:
    
    - `mutate` → `dispatch_mutate`
    - `maybe_inplace_mutate` → `dispatch_maybe_inplace_mutate`
    
    The user-facing recursive methods are renamed to:
    
    - `mutate_child` → `mutate`
    - `maybe_inplace_mutate_child` → `maybe_inplace_mutate`
    
    This makes the correspondence with the C++ protocol clearer:
    
    - C++ vtable dispatch → Rust `dispatch_mutate`
    - Recursive mutation → Rust `mutate`
    - Default mutation of the current value → Rust `default_mutate`
    
    This PR also adds `default_mutate_value`, allowing a typed
    `#[dispatch(mutate)]` handler to apply default recursion to its current
    value before performing a post-order rewrite.
    
    The dispatch macro, documentation, and tests are updated accordingly.
    All Rust workspace tests pass.
---
 docs/guides/rust_lang_guide.md               |  16 ++---
 rust/tvm-ffi-macros/src/dispatch.rs          |   4 +-
 rust/tvm-ffi/src/extra/structural_mutate.rs  | 101 +++++++++++++++------------
 rust/tvm-ffi/tests/test_structural_mutate.rs |  87 +++++++++++++++--------
 4 files changed, 126 insertions(+), 82 deletions(-)

diff --git a/docs/guides/rust_lang_guide.md b/docs/guides/rust_lang_guide.md
index cae1c758..92f2d059 100644
--- a/docs/guides/rust_lang_guide.md
+++ b/docs/guides/rust_lang_guide.md
@@ -525,13 +525,13 @@ assert_eq!(mutated.iter().collect::<Vec<_>>(), vec![2, 
3]);
 assert_eq!(mutator.state().integers, 2);
 ```
 
-`MutateContext::mutate` uses the copy path for a borrowed child, while
-`maybe_inplace_mutate` preserves the reuse opportunity of an owned child.
+`MutateContext::mutate` uses the copy path for a borrowed value, while
+`maybe_inplace_mutate` preserves the reuse opportunity of an owned value.
 Callbacks are `Fn`; mutable data belongs in the mutator state.
 
 For ordinary mutable state, `#[dispatch(mutate)]` generates a
 `StructuralMutator` from `mutate_*` methods. A matching handler returns the
-current value's final result and may recursively call `self.mutate_child()`;
+current value's final result and may recursively call `self.mutate()`;
 an unmatched value follows default mutation with its current in-place permit:
 
 ```rust
@@ -563,10 +563,10 @@ assert_eq!(increment.integers, 2);
 For a named custom recursion policy, implement `StructuralMutator` and pass
 `&mut` it to `structural_mutate`. `InplaceValue` is an engine-issued
 capability: callers cannot construct it from a read-only `MapValue`. Override
-`maybe_inplace_mutate` to opt into default container reuse;
+`dispatch_maybe_inplace_mutate` to opt into default container reuse;
 `default_maybe_inplace_mutate` rechecks uniqueness before writing. Borrowed
-children can be re-entered with `mutate_child`, while owned children can use
-`maybe_inplace_mutate_child`:
+values can be re-entered with `mutate`, while owned values can use
+`maybe_inplace_mutate`:
 
 ```rust
 use tvm_ffi::{
@@ -578,14 +578,14 @@ use tvm_ffi::{
 struct Increment;
 
 impl StructuralMutator for Increment {
-    fn mutate(&mut self, value: &MapValue, kind: DefRegionKind) -> Result<Any> 
{
+    fn dispatch_mutate(&mut self, value: &MapValue, kind: DefRegionKind) -> 
Result<Any> {
         match value.cast::<i64>() {
             Some(value) => Ok(Any::from(value + 1)),
             None => self.default_mutate(value, kind),
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         kind: DefRegionKind,
diff --git a/rust/tvm-ffi-macros/src/dispatch.rs 
b/rust/tvm-ffi-macros/src/dispatch.rs
index 0e692c3d..21c6ccb4 100644
--- a/rust/tvm-ffi-macros/src/dispatch.rs
+++ b/rust/tvm-ffi-macros/src/dispatch.rs
@@ -244,7 +244,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
                 {
                     #[inline]
                     #[allow(unreachable_code, unused_variables)]
-                    fn mutate(
+                    fn dispatch_mutate(
                         &mut self,
                         value: &#tvm_ffi::extra::structural_mutate::MapValue,
                         def_region_kind: 
#tvm_ffi::extra::structural_visit::DefRegionKind,
@@ -256,7 +256,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
 
                     #[inline]
                     #[allow(unreachable_code, unused_variables)]
-                    fn maybe_inplace_mutate(
+                    fn dispatch_maybe_inplace_mutate(
                         &mut self,
                         value: 
#tvm_ffi::extra::structural_mutate::InplaceValue<'_>,
                         def_region_kind: 
#tvm_ffi::extra::structural_visit::DefRegionKind,
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs 
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 1c6b0c3e..7d5b607a 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -135,40 +135,40 @@ impl<State> MutateContext<'_, State> {
         self.def_region_kind
     }
 
-    /// Mutate a borrowed child through the same callback chain. The child and
+    /// Mutate a borrowed value through the same callback chain. The value and
     /// its descendants begin on the non-in-place path.
-    pub fn mutate<T>(&mut self, child: &T) -> Result<Any>
+    pub fn mutate<T>(&mut self, value: &T) -> Result<Any>
     where
         for<'x> AnyView<'x>: From<&'x T>,
     {
-        self.mutate_with(child, self.def_region_kind)
+        self.mutate_with(value, self.def_region_kind)
     }
 
-    /// Mutate a borrowed child under an explicit definition-region state.
-    pub fn mutate_with<T>(&mut self, child: &T, def_region_kind: 
DefRegionKind) -> Result<Any>
+    /// Mutate a borrowed value under an explicit definition-region state.
+    pub fn mutate_with<T>(&mut self, value: &T, def_region_kind: 
DefRegionKind) -> Result<Any>
     where
         for<'x> AnyView<'x>: From<&'x T>,
     {
-        let view = AnyView::from(child);
+        let view = AnyView::from(value);
         self.driver
             .mutate_raw(*view.as_raw_ffi_any(), def_region_kind, Permit::Copy)
     }
 
-    /// Mutate an owned child, allowing an in-place attempt when it remains
+    /// Mutate an owned value, allowing an in-place attempt when it remains
     /// uniquely owned and no matched callback borrows it.
-    pub fn maybe_inplace_mutate<T: Into<Any>>(&mut self, child: T) -> 
Result<Any> {
-        self.maybe_inplace_mutate_with(child, self.def_region_kind)
+    pub fn maybe_inplace_mutate<T: Into<Any>>(&mut self, value: T) -> 
Result<Any> {
+        self.maybe_inplace_mutate_with(value, self.def_region_kind)
     }
 
-    /// Mutate an owned child under an explicit definition-region state.
+    /// Mutate an owned value under an explicit definition-region state.
     pub fn maybe_inplace_mutate_with<T: Into<Any>>(
         &mut self,
-        child: T,
+        value: T,
         def_region_kind: DefRegionKind,
     ) -> Result<Any> {
-        let child = child.into();
+        let value = value.into();
         self.driver.mutate_raw(
-            *child.as_raw_ffi_any(),
+            *value.as_raw_ffi_any(),
             def_region_kind,
             Permit::MaybeInPlace,
         )
@@ -883,49 +883,47 @@ impl StructuralVarRemap {
 
 /// A mutator that controls its own recursion.
 ///
-/// Implementations descend with the child or `default_*` helpers.
+/// Implementations descend with the `mutate` or `default_*` helpers.
 /// `#[dispatch(mutate)]` generates this trait from typed `mutate_*` methods.
 pub trait StructuralMutator: Sized {
-    /// Mutate one borrowed value without modifying its source storage.
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any>;
+    /// Dispatch one borrowed value without modifying its source storage.
+    ///
+    /// The structural-mutation engine calls this hook for each value.
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any>;
 
-    /// Mutate one value for which the engine permits an in-place attempt.
+    /// Dispatch one value for which the engine permits an in-place attempt.
     ///
-    /// The default delegates to [`Self::mutate`] and therefore remains
+    /// The default delegates to [`Self::dispatch_mutate`] and therefore 
remains
     /// non-in-place. Override this method to opt into the default container
     /// reuse path.
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
     ) -> Result<Any> {
-        self.mutate(value.as_value(), def_region_kind)
+        self.dispatch_mutate(value.as_value(), def_region_kind)
     }
 
-    /// Re-enter this mutator for a borrowed child. The child and all of its
+    /// Re-enter this mutator for a borrowed value. The value and all of its
     /// descendants use the non-in-place path.
-    fn mutate_child<T>(&mut self, child: &T, def_region_kind: DefRegionKind) 
-> Result<Any>
+    fn mutate<T>(&mut self, value: &T, def_region_kind: DefRegionKind) -> 
Result<Any>
     where
         for<'x> AnyView<'x>: From<&'x T>,
     {
-        let view = AnyView::from(child);
+        let view = AnyView::from(value);
         dispatch_user_raw(self, *view.as_raw_ffi_any(), def_region_kind, 
Permit::Copy)
     }
 
-    /// Re-enter this mutator for an owned child, permitting reuse only when
+    /// Re-enter this mutator for an owned value, permitting reuse only when
     /// the converted value remains uniquely owned.
-    fn maybe_inplace_mutate_child<T>(
-        &mut self,
-        child: T,
-        def_region_kind: DefRegionKind,
-    ) -> Result<Any>
+    fn maybe_inplace_mutate<T>(&mut self, value: T, def_region_kind: 
DefRegionKind) -> Result<Any>
     where
         T: Into<Any>,
     {
-        let child = child.into();
+        let value = value.into();
         dispatch_user_raw(
             self,
-            *child.as_raw_ffi_any(),
+            *value.as_raw_ffi_any(),
             def_region_kind,
             Permit::MaybeInPlace,
         )
@@ -936,6 +934,20 @@ pub trait StructuralMutator: Sized {
         user_default_mutate(self, value.raw(), def_region_kind, Permit::Copy)
     }
 
+    /// Apply default non-in-place mutation to a borrowed typed value.
+    ///
+    /// Unlike [Self::mutate], this bypasses dispatch for the value
+    /// itself while its children still re-enter this mutator. This lets a
+    /// typed structural-mutate handler recurse through its current node
+    /// before applying a post-order rewrite.
+    fn default_mutate_value<T>(&mut self, value: &T, def_region_kind: 
DefRegionKind) -> Result<Any>
+    where
+        for<'x> AnyView<'x>: From<&'x T>,
+    {
+        let view = AnyView::from(value);
+        user_default_mutate(self, *view.as_raw_ffi_any(), def_region_kind, 
Permit::Copy)
+    }
+
     /// Apply the default mutation under an engine-issued in-place capability.
     ///
     /// Uniqueness is checked again here because user code may have retained
@@ -989,7 +1001,7 @@ impl<State, Link, Marker> StructuralMutator for 
MutateCallbacks<State, Link, Mar
 where
     Link: MutateChainLink<State, Marker>,
 {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         let callback_ptr = Rc::as_ptr(&self.callbacks);
         match try_mutate_callbacks::<State, Link, Marker>(
             self,
@@ -1002,7 +1014,7 @@ where
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -1024,7 +1036,7 @@ impl<Link, Marker> StructuralMutator for 
DirectMutateCallbacks<'_, Link, Marker>
 where
     Link: MutateChainLink<(), Marker>,
 {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         let callback_ptr = std::ptr::from_ref(self.callbacks);
         match try_mutate_callbacks::<(), Link, Marker>(self, callback_ptr, 
value, def_region_kind) {
             Some(result) => result,
@@ -1032,7 +1044,7 @@ where
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -1407,13 +1419,13 @@ struct StructuralMutatorVTable {
     var_remap_set: FStructuralVarRemapSet,
 }
 
-type RuntimeMutateCallback =
+type RuntimeDispatchMutateCallback =
     unsafe fn(*mut c_void, TVMFFIAny, DefRegionKind, Permit) -> Result<Any>;
 type RuntimeVarRemapGetCallback = unsafe fn(*mut c_void, TVMFFIAny) -> 
Result<Option<Any>>;
 type RuntimeVarRemapSetCallback = unsafe fn(*mut c_void, TVMFFIAny, &Any) -> 
Result<()>;
 
 struct RuntimeMutatorCallbacks {
-    mutate: RuntimeMutateCallback,
+    dispatch_mutate: RuntimeDispatchMutateCallback,
     var_remap_get: RuntimeVarRemapGetCallback,
     var_remap_set: RuntimeVarRemapSetCallback,
 }
@@ -1561,7 +1573,7 @@ unsafe fn rust_vtable_mutate_impl(
         Err(error) => return result_into_raw(Err(error)),
     };
     let context = context_guard.context;
-    let callback = (*mutator).callbacks.mutate;
+    let callback = (*mutator).callbacks.dispatch_mutate;
     let raw = *value.as_raw_ffi_any();
     let outcome = catch_unwind(AssertUnwindSafe(|| {
         let kind = def_region_from_raw((*mutator).def_region_mode)?;
@@ -1822,7 +1834,7 @@ impl<U: StructuralMutator> MutationDriver for U {
 ///
 /// `context` must come from the current mutable reborrow of a live `D`; the
 /// runtime object hides that pointer until this call returns.
-unsafe fn runtime_mutate<D: MutationDriver>(
+unsafe fn runtime_dispatch_mutate<D: MutationDriver>(
     context: *mut c_void,
     raw: TVMFFIAny,
     def_region_kind: DefRegionKind,
@@ -1835,7 +1847,7 @@ unsafe fn runtime_mutate<D: MutationDriver>(
 ///
 /// # Safety
 ///
-/// `context` must satisfy the same requirements as [`runtime_mutate`].
+/// `context` must satisfy the same requirements as 
[`runtime_dispatch_mutate`].
 unsafe fn runtime_var_remap_get<D: MutationDriver>(
     context: *mut c_void,
     raw: TVMFFIAny,
@@ -1847,7 +1859,7 @@ unsafe fn runtime_var_remap_get<D: MutationDriver>(
 ///
 /// # Safety
 ///
-/// `context` must satisfy the same requirements as [`runtime_mutate`], and
+/// `context` must satisfy the same requirements as 
[`runtime_dispatch_mutate`], and
 /// `replacement` must remain alive for this call.
 unsafe fn runtime_var_remap_set<D: MutationDriver>(
     context: *mut c_void,
@@ -1860,7 +1872,7 @@ unsafe fn runtime_var_remap_set<D: MutationDriver>(
 fn run_structural_mutator<D: MutationDriver>(root: Any, driver: &mut D) -> 
Result<Any> {
     let context = std::ptr::from_mut(driver).cast::<c_void>();
     let callbacks = RuntimeMutatorCallbacks {
-        mutate: runtime_mutate::<D>,
+        dispatch_mutate: runtime_dispatch_mutate::<D>,
         var_remap_get: runtime_var_remap_get::<D>,
         var_remap_set: runtime_var_remap_set::<D>,
     };
@@ -2066,9 +2078,10 @@ fn dispatch_user_raw<U: StructuralMutator>(
 ) -> Result<Any> {
     let result = if permit == Permit::MaybeInPlace && object_is_unique(raw) {
         let mut scoped_raw = raw;
-        mutator.maybe_inplace_mutate(InplaceValue::from_raw(&mut scoped_raw), 
def_region_kind)
+        mutator
+            .dispatch_maybe_inplace_mutate(InplaceValue::from_raw(&mut 
scoped_raw), def_region_kind)
     } else {
-        mutator.mutate(&MapValue::from_raw(raw), def_region_kind)
+        mutator.dispatch_mutate(&MapValue::from_raw(raw), def_region_kind)
     };
     result.map_err(|error| with_value_context(error, raw))
 }
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs 
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index 0e4fea5e..11934875 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -550,7 +550,7 @@ struct ManualIncrement {
 }
 
 impl StructuralMutator for ManualIncrement {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         if let Some(integer) = value.cast::<i64>() {
             Ok(Any::from(integer + 1))
         } else {
@@ -558,7 +558,7 @@ impl StructuralMutator for ManualIncrement {
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -582,7 +582,7 @@ struct ReplaceNone {
 }
 
 impl StructuralMutator for ReplaceNone {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         if value.type_index() == TypeIndex::kTVMFFINone as i32 {
             self.calls += 1;
             Ok(Any::from(8i64))
@@ -591,7 +591,7 @@ impl StructuralMutator for ReplaceNone {
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -615,7 +615,7 @@ struct RemappingFreeVar {
 }
 
 impl StructuralMutator for RemappingFreeVar {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         if value.type_index() == self.type_index {
             if let Some(mutated) = self.remap.get(value)? {
                 return Ok(mutated);
@@ -629,7 +629,7 @@ impl StructuralMutator for RemappingFreeVar {
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -646,21 +646,21 @@ impl StructuralMutator for RemappingFreeVar {
     }
 }
 
-struct ChildHelperMutator {
+struct RecursiveEntryMutator {
     remap: StructuralVarRemap,
-    use_owned_child: bool,
-    owned_child_pointer: Option<usize>,
+    use_owned_value: bool,
+    owned_value_pointer: Option<usize>,
 }
 
-impl StructuralMutator for ChildHelperMutator {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+impl StructuralMutator for RecursiveEntryMutator {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         if value.type_index() == TypeIndex::kTVMFFINone as i32 {
-            if self.use_owned_child {
-                let child = Array::new(vec![1i64]);
-                self.owned_child_pointer = Some(array_pointer(&child) as 
usize);
-                self.maybe_inplace_mutate_child(child, def_region_kind)
+            if self.use_owned_value {
+                let value = Array::new(vec![1i64]);
+                self.owned_value_pointer = Some(array_pointer(&value) as 
usize);
+                self.maybe_inplace_mutate(value, def_region_kind)
             } else {
-                self.mutate_child(&1i64, def_region_kind)
+                self.mutate(&1i64, def_region_kind)
             }
         } else if let Some(integer) = value.cast::<i64>() {
             Ok(Any::from(integer + 1))
@@ -669,7 +669,7 @@ impl StructuralMutator for ChildHelperMutator {
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -693,7 +693,7 @@ struct RejectAliasedReentry {
 }
 
 impl StructuralMutator for RejectAliasedReentry {
-    fn mutate(&mut self, value: &MapValue, def_region_kind: DefRegionKind) -> 
Result<Any> {
+    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
         if let Some(integer) = value.cast::<i64>() {
             let retained = RETAINED_MUTATOR.with(|slot| 
slot.borrow().as_ref().unwrap().clone());
             let error = match 
Function::get_global("ffi.StructuralMutatorMutate")
@@ -713,7 +713,7 @@ impl StructuralMutator for RejectAliasedReentry {
         }
     }
 
-    fn maybe_inplace_mutate(
+    fn dispatch_maybe_inplace_mutate(
         &mut self,
         value: InplaceValue<'_>,
         def_region_kind: DefRegionKind,
@@ -878,28 +878,28 @@ fn user_mutator_can_store_a_changed_free_var_result() {
 }
 
 #[test]
-fn user_mutator_child_helpers_reenter_the_same_mutator() {
-    let mut borrowed = ChildHelperMutator {
+fn user_mutator_recursive_entries_reenter_the_same_mutator() {
+    let mut borrowed = RecursiveEntryMutator {
         remap: StructuralVarRemap::default(),
-        use_owned_child: false,
-        owned_child_pointer: None,
+        use_owned_value: false,
+        owned_value_pointer: None,
     };
     let mutated = structural_mutate(Any::new(), &mut borrowed)
         .and_then(i64::try_from)
         .unwrap();
     assert_eq!(mutated, 2);
 
-    let mut owned = ChildHelperMutator {
+    let mut owned = RecursiveEntryMutator {
         remap: StructuralVarRemap::default(),
-        use_owned_child: true,
-        owned_child_pointer: None,
+        use_owned_value: true,
+        owned_value_pointer: None,
     };
     let mutated = structural_mutate(Any::new(), &mut owned)
         .and_then(Array::<i64>::try_from)
         .unwrap();
     assert_eq!(
         array_pointer(&mutated) as usize,
-        owned.owned_child_pointer.unwrap()
+        owned.owned_value_pointer.unwrap()
     );
     assert_eq!(mutated.get(0).unwrap(), 2);
 }
@@ -1435,7 +1435,7 @@ impl GeneratedRecursiveMutator {
     fn mutate_array(&mut self, array: Array<i64>, kind: DefRegionKind) -> 
Result<Any> {
         let mut mutated = Vec::with_capacity(array.len());
         for value in array.iter() {
-            mutated.push(i64::try_from(self.mutate_child(&value, kind)?)?);
+            mutated.push(i64::try_from(self.mutate(&value, kind)?)?);
         }
         Ok(Any::from(Array::new(mutated)))
     }
@@ -1457,6 +1457,37 @@ fn 
generated_mutator_can_drive_recursion_through_mut_self() {
     assert_eq!(mutator.integers, vec![1, 2]);
 }
 
+#[derive(Default)]
+struct GeneratedDefaultingMutator {
+    arrays: usize,
+    integers: Vec<i64>,
+}
+
+#[dispatch(mutate)]
+impl GeneratedDefaultingMutator {
+    fn mutate_array(&mut self, array: Array<i64>, kind: DefRegionKind) -> 
Result<Any> {
+        self.arrays += 1;
+        self.default_mutate_value(&array, kind)
+    }
+
+    fn mutate_integer(&mut self, value: i64) -> Any {
+        self.integers.push(value);
+        Any::from(value + 1)
+    }
+}
+
+#[test]
+fn generated_mutator_can_default_recurse_from_a_typed_handler() {
+    let mut mutator = GeneratedDefaultingMutator::default();
+    let mutated = structural_mutate(Array::new(vec![1i64, 2]), &mut mutator)
+        .and_then(Array::<i64>::try_from)
+        .unwrap();
+
+    assert_eq!(mutated.iter().collect::<Vec<_>>(), vec![2, 3]);
+    assert_eq!(mutator.arrays, 1);
+    assert_eq!(mutator.integers, vec![1, 2]);
+}
+
 struct GeneratedRemappingMutator {
     type_index: i32,
     calls: usize,

Reply via email to