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 82efff9a [Rust] match_any: fewer runtime helpers; check the table key 
once (#965)
82efff9a is described below

commit 82efff9a04f842c6224a1abfaa40b85a5603245f
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun Nov 8 10:29:29 2026 -0500

    [Rust] match_any: fewer runtime helpers; check the table key once (#965)
    
    Simplify the `match_any!` runtime helpers and make the lookup table pay
    its key check once.
    
    - An owned scrutinee stays in its probe and is matched through
    `ViewSource`; `OwnedSource` is removed.
    - A typed `MutationView` arm aliases the source instead of moving it
    out, so a rejected guard has nothing to restore.
    `MatchPattern::try_match` returns `Option<Self>`; `Matched`, `Restore`,
    the `Recovery` type and `DispatchRecovery` are removed.
    - Value arms key the lookup table by their own type, removing
    `StrictLeaf` and the hidden `MATCH_ANY_STRICT_EXACT` constant (it always
    equaled `MATCH_ANY_EXACT`).
    - The table's key check (one type-index load per arm) runs once when the
    call site's table is built rather than on every match.
    - Remove the unused `UnchangedOr::try_map` and `UnchangedOr::try_cast`.
---
 rust/tvm-ffi-macros/src/dispatch.rs          |   5 +-
 rust/tvm-ffi-macros/src/match_any.rs         |  40 ++---
 rust/tvm-ffi/src/extra/dispatch.rs           |  14 --
 rust/tvm-ffi/src/extra/structural_mutate.rs  |  14 +-
 rust/tvm-ffi/src/extra/unchanged.rs          |  76 +--------
 rust/tvm-ffi/src/match_any_internal.rs       | 220 ++++++---------------------
 rust/tvm-ffi/src/object.rs                   |   1 -
 rust/tvm-ffi/src/type_traits.rs              |   8 -
 rust/tvm-ffi/tests/test_structural_mutate.rs |   4 +-
 9 files changed, 82 insertions(+), 300 deletions(-)

diff --git a/rust/tvm-ffi-macros/src/dispatch.rs 
b/rust/tvm-ffi-macros/src/dispatch.rs
index f2bbee50..83ffd8d0 100644
--- a/rust/tvm-ffi-macros/src/dispatch.rs
+++ b/rust/tvm-ffi-macros/src/dispatch.rs
@@ -194,11 +194,8 @@ fn expand(item: &ItemImpl, args: &DispatchArgs) -> 
syn::Result<TokenStream2> {
             && has_pattern_lifetime(&ty)
         {
             lifetimes.normalize(&mut ty, None);
-            // Fix recovery independently of the arm-local mutable source 
borrow.
-            let recovery =
-                quote!(<#ty as 
#tvm_ffi::extra::dispatch::DispatchRecovery<#source>>::Recovery);
             generics.make_where_clause().predicates.push(syn::parse_quote!(
-                for<#borrow> #ty: 
#tvm_ffi::match_any_internal::MatchPattern<&#borrow mut #source, Recovery = 
#recovery>
+                for<#borrow> #ty: 
#tvm_ffi::match_any_internal::MatchPattern<&#borrow mut #source>
             ));
         }
         patterns.push(ty);
diff --git a/rust/tvm-ffi-macros/src/match_any.rs 
b/rust/tvm-ffi-macros/src/match_any.rs
index 3522af8d..992bc079 100644
--- a/rust/tvm-ffi-macros/src/match_any.rs
+++ b/rust/tvm-ffi-macros/src/match_any.rs
@@ -160,15 +160,11 @@ fn expand_match_any(input: MatchAnyInput) -> TokenStream {
         let conversion = if can_lookup {
             quote!(if #lookup.is_err() || #lookup == 
::core::result::Result::Ok(::core::option::Option::Some(#id)) {
                 #conversion
-            } else { ::core::result::Result::Err(()) })
+            } else { ::core::option::Option::None })
         } else { conversion };
         let rejected = Ident::new("__tvm_ffi_match_any_rejected", span);
-        let recovered = Ident::new("__tvm_ffi_match_any_recovered", span);
-        let recover = quote! {
-            if let ::core::result::Result::Ok(#recovered) = 
#rejected.map(#tvm_ffi::match_any_internal::Matched::recover) {
-                #tvm_ffi::match_any_internal::Restore::restore(&mut #source, 
#recovered);
-            }
-        };
+        // Drop a rejected value before later arms check uniqueness.
+        let drop_rejected = quote!(#[allow(clippy::drop_non_drop)] 
::core::mem::drop(#rejected););
         if let Some(binding) = shared_ref_binding(binding) {
             let holder = Ident::new("__tvm_ffi_match_any_ref_holder", span);
             let converted = Ident::new("__tvm_ffi_match_any_ref_converted", 
span);
@@ -176,28 +172,28 @@ fn expand_match_any(input: MatchAnyInput) -> TokenStream {
             let test_guard = arm.guard.as_ref().map(|guard| 
quote!(#guard)).unwrap_or_else(|| quote!(true));
             quote! {{
                 let #converted = #conversion;
-                let #accepted = if let ::core::result::Result::Ok(#holder) = 
&#converted {
+                let #accepted = if let ::core::option::Option::Some(#holder) = 
&#converted {
                     #[allow(unused_variables)]
-                    let #binding: &#matcher = 
::core::borrow::Borrow::<#matcher>::borrow(&#holder.value);
+                    let #binding: &#matcher = 
::core::borrow::Borrow::<#matcher>::borrow(#holder);
                     #test_guard
                 } else { false };
                 if #accepted {
                     let #holder = #converted.as_ref().unwrap();
                     #[allow(unused_variables)]
-                    let #binding: &#matcher = 
::core::borrow::Borrow::<#matcher>::borrow(&#holder.value);
+                    let #binding: &#matcher = 
::core::borrow::Borrow::<#matcher>::borrow(#holder);
                     #body
                 } else {
                     let #rejected = #converted;
-                    #recover
+                    #drop_rejected
                     #next
                 }
             }}
         } else {
             quote! {
                 match #conversion {
-                    
::core::result::Result::Ok(#tvm_ffi::match_any_internal::Matched { value: 
#binding, .. }) #guard => { #body },
+                    ::core::option::Option::Some(#binding) #guard => { #body },
                     #rejected => {
-                        #recover
+                        #drop_rejected
                         #next
                     }
                 }
@@ -253,7 +249,7 @@ fn expand_leaf_table_lookup(tvm_ffi: &TokenStream, arms: 
&[TypedArm], view: &Ide
                     (&&&probe).leaf_pattern()
                 })
             } else {
-                
quote!(::core::marker::PhantomData::<#tvm_ffi::match_any_internal::StrictLeaf<#matcher>>)
+                quote!(::core::marker::PhantomData::<#matcher>)
             };
             quote!(#tail.prepend(#metadata))
         },
@@ -263,13 +259,19 @@ fn expand_leaf_table_lookup(tvm_ffi: &TokenStream, arms: 
&[TypedArm], view: &Ide
         let #probe = #pattern_probe;
         match (&#probe).leaf_pattern_list_id() {
             ::core::option::Option::Some(#pattern_list_id) => {
-                static #static_table: 
::std::sync::OnceLock<#tvm_ffi::match_any_internal::LeafLookupTable> = 
::std::sync::OnceLock::new();
+                // Type indices are process-stable, so the key is checked once.
+                static #static_table: 
::std::sync::OnceLock<::core::option::Option<#tvm_ffi::match_any_internal::LeafLookupTable>>
 = ::std::sync::OnceLock::new();
                 let #table = #static_table.get_or_init(|| {
-                    let mut #type_indices = [0_i32; #arm_count];
-                    (&#probe).fill_leaf_type_indices(&mut #type_indices);
-                    
#tvm_ffi::match_any_internal::LeafLookupTable::build(#pattern_list_id, 
&#type_indices)
+                    (&#probe).matches_key().then(|| {
+                        let mut #type_indices = [0_i32; #arm_count];
+                        (&#probe).fill_leaf_type_indices(&mut #type_indices);
+                        
#tvm_ffi::match_any_internal::LeafLookupTable::build(#pattern_list_id, 
&#type_indices)
+                    })
                 });
-                #table.lookup(#pattern_list_id, #view.type_index())
+                match #table {
+                    ::core::option::Option::Some(#table) => 
#table.lookup(#pattern_list_id, #view.type_index()),
+                    ::core::option::Option::None => 
::core::result::Result::Err(()),
+                }
             }
             ::core::option::Option::None => ::core::result::Result::Err(()),
         }
diff --git a/rust/tvm-ffi/src/extra/dispatch.rs 
b/rust/tvm-ffi/src/extra/dispatch.rs
index d82ad536..378acc34 100644
--- a/rust/tvm-ffi/src/extra/dispatch.rs
+++ b/rust/tvm-ffi/src/extra/dispatch.rs
@@ -56,20 +56,6 @@ impl<'a, T> DispatchInput for crate::MutationView<'a, T> {
     type Source = crate::match_any_internal::MutationSource<'a>;
 }
 
-// Generated alias bounds must not make recovery depend on a temporary borrow.
-#[doc(hidden)]
-pub trait DispatchRecovery<S> {
-    type Recovery;
-}
-impl<S, T: crate::type_traits::ContainerElement> DispatchRecovery<S> for T {
-    type Recovery = ();
-}
-impl<'source, 'value> 
DispatchRecovery<crate::match_any_internal::ViewSource<'source>>
-    for AnyView<'value>
-{
-    type Recovery = ();
-}
-
 use std::{borrow::BorrowMut, marker::PhantomData};
 
 /// Inferred selectors for dispatcher normalization.
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs 
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 156b50bd..dc80da21 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -358,6 +358,13 @@ impl<'a, T> MutationView<'a, T> {
         }
     }
 
+    /// # Safety
+    /// The original must not be used while the alias lives.
+    #[inline(always)]
+    pub(crate) unsafe fn alias(&self) -> MutationView<'a, T> {
+        MutationView::from_raw(self.value, self.mode)
+    }
+
     // A chain may lend its view to one link at a time. The shorter lifetime
     // prevents a matched link from retaining a borrow across the next link.
     #[inline(always)]
@@ -566,13 +573,6 @@ impl<T: object::ObjectRefCore> MutationView<'_, T> {
     }
 }
 
-impl<'source, 'value, T: crate::type_traits::ContainerElement>
-    
super::dispatch::DispatchRecovery<crate::match_any_internal::MutationSource<'source>>
-    for crate::MutationView<'value, T>
-{
-    type Recovery = crate::MutationView<'source>;
-}
-
 /// The running structural mutation, passed to each handler.
 ///
 /// A handler that matches a node decides how its children are rewritten: call
diff --git a/rust/tvm-ffi/src/extra/unchanged.rs 
b/rust/tvm-ffi/src/extra/unchanged.rs
index bff097a2..5bca9555 100644
--- a/rust/tvm-ffi/src/extra/unchanged.rs
+++ b/rust/tvm-ffi/src/extra/unchanged.rs
@@ -24,7 +24,7 @@ use std::marker::PhantomData;
 use super::structural_common::{raw_ffi_any_same_as, structural_error_from_raw};
 
 use crate::any::{Any, AnyView, TryFromTemp};
-use crate::error::{Error, Result, TYPE_ERROR};
+use crate::error::{Error, Result};
 use crate::tvm_ffi_sys::{TVMFFIAny, TVMFFITypeIndex};
 use crate::type_traits::{AnyCompatible, ContainerElement};
 
@@ -209,80 +209,6 @@ impl<T: ContainerElement> UnchangedOr<T> {
             None => UnchangedOr::unchanged(),
         }
     }
-
-    /// Fallibly convert only the replacement, preserving an unchanged result.
-    ///
-    /// # Arguments
-    ///
-    /// * `convert` - Converts the replacement; called only when there is one.
-    ///
-    /// # Returns
-    ///
-    /// The converted result.
-    ///
-    /// # Errors
-    ///
-    /// Returns the error of `convert` unchanged.
-    ///
-    /// ```
-    /// use tvm_ffi::UnchangedOr;
-    /// let result = 
UnchangedOr::changed(3_i64).try_map(u32::try_from).unwrap();
-    /// assert_eq!(result.into_option(), Some(3_u32));
-    /// ```
-    ///
-    /// See also [`UnchangedOr`].
-    #[inline]
-    pub fn try_map<U: ContainerElement, E>(
-        self,
-        convert: impl FnOnce(T) -> std::result::Result<U, E>,
-    ) -> std::result::Result<UnchangedOr<U>, E> {
-        match self.into_option() {
-            Some(value) => convert(value).map(UnchangedOr::changed),
-            None => Ok(UnchangedOr::unchanged()),
-        }
-    }
-
-    /// Check a replacement's FFI type without cloning it.
-    ///
-    /// An unchanged result is compatible with every replacement type. Erased 
and
-    /// narrowing conversions retain the strict type check of
-    /// [`Any::downcast_ref`]. For a
-    /// known typed conversion, use `map(Into::into)`.
-    ///
-    /// # Returns
-    ///
-    /// The result typed as `UnchangedOr<U>`.
-    ///
-    /// # Errors
-    ///
-    /// Returns a `TypeError` if the replacement is not stored as `U`.
-    ///
-    /// ```
-    /// use tvm_ffi::{Any, UnchangedOr};
-    /// let erased: UnchangedOr<Any> = 7_i64.into();
-    /// assert_eq!(erased.try_cast::<i64>()?.into_option(), Some(7));
-    /// # Ok::<(), tvm_ffi::Error>(())
-    /// ```
-    ///
-    /// See also [`UnchangedOr`].
-    #[inline]
-    pub fn try_cast<U: ContainerElement>(self) -> Result<UnchangedOr<U>> {
-        if unsafe { 
UnchangedOr::<U>::check_any_strict(self.data.as_raw_ffi_any()) } {
-            Ok(UnchangedOr {
-                data: self.data,
-                _marker: PhantomData,
-            })
-        } else {
-            Err(Error::new(
-                TYPE_ERROR,
-                &format!(
-                    "structural mutation result does not match {}",
-                    U::container_type_str()
-                ),
-                "",
-            ))
-        }
-    }
 }
 
 impl UnchangedOr<Any> {
diff --git a/rust/tvm-ffi/src/match_any_internal.rs 
b/rust/tvm-ffi/src/match_any_internal.rs
index 2de5fc2a..cbb6425c 100644
--- a/rust/tvm-ffi/src/match_any_internal.rs
+++ b/rust/tvm-ffi/src/match_any_internal.rs
@@ -45,31 +45,23 @@ impl<T> SourceProbe<T> {
     }
 }
 
-#[doc(hidden)]
-pub struct OwnedSource(crate::Any);
 #[doc(hidden)]
 pub struct ViewSource<'a>(AnyView<'a>);
 #[doc(hidden)]
-pub struct MutationSource<'a>(Option<crate::MutationView<'a>>);
+pub struct MutationSource<'a>(crate::MutationView<'a>);
 
-/// Object references move into an owning Any; inherent adapters preserve view 
and
-/// view lifetimes independently of the local holder's lifetime.
+/// An owned scrutinee stays in the probe and is viewed in place.
 #[doc(hidden)]
 pub trait PrepareSource {
-    type Source;
-    fn prepare(&mut self) -> Self::Source;
+    fn prepare(&mut self) -> ViewSource<'_>;
 }
-impl<T: crate::ObjectRefCore> PrepareSource for SourceProbe<T> {
-    type Source = OwnedSource;
-    #[inline(always)]
-    fn prepare(&mut self) -> Self::Source {
-        OwnedSource(self.0.take().unwrap().into())
-    }
-}
-impl SourceProbe<crate::Any> {
+impl<T> PrepareSource for SourceProbe<T>
+where
+    for<'a> AnyView<'a>: From<&'a T>,
+{
     #[inline(always)]
-    pub fn prepare(&mut self) -> OwnedSource {
-        OwnedSource(self.0.take().unwrap())
+    fn prepare(&mut self) -> ViewSource<'_> {
+        ViewSource(self.0.as_ref().unwrap().into())
     }
 }
 impl<'a> SourceProbe<AnyView<'a>> {
@@ -90,7 +82,7 @@ impl<'a, T: ?Sized> SourceProbe<&'a T> {
 impl<'a, T> SourceProbe<crate::MutationView<'a, T>> {
     #[inline(always)]
     pub fn prepare(&mut self) -> MutationSource<'a> {
-        MutationSource(Some(self.0.take().unwrap().into_untyped()))
+        MutationSource(self.0.take().unwrap().into_untyped())
     }
 }
 
@@ -100,16 +92,6 @@ pub trait SourceView {
     /// The source's own cell, which a reference pattern borrows in place.
     fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny;
 }
-impl SourceView for OwnedSource {
-    #[inline(always)]
-    fn view(&self) -> AnyView<'_> {
-        (&self.0).into()
-    }
-    #[inline(always)]
-    fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny {
-        self.0.as_raw_ffi_any()
-    }
-}
 impl SourceView for ViewSource<'_> {
     #[inline(always)]
     fn view(&self) -> AnyView<'_> {
@@ -123,67 +105,29 @@ impl SourceView for ViewSource<'_> {
 impl SourceView for MutationSource<'_> {
     #[inline(always)]
     fn view(&self) -> AnyView<'_> {
-        self.0.as_ref().unwrap().as_any_view()
+        self.0.as_any_view()
     }
     #[inline(always)]
     fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny {
-        self.0.as_ref().unwrap().cell()
+        self.0.cell()
     }
 }
 
-/// A rejected guard drops an ordinary result or returns the original view.
-/// There is no Drop implementation, so native match guards retain Rust's usual
-/// restriction against moving their bindings before the guard succeeds.
-#[doc(hidden)]
-pub struct Matched<T, R> {
-    pub value: T,
-    recover: fn(T) -> R,
-}
-impl<T, R> Matched<T, R> {
-    #[inline(always)]
-    pub fn recover(self) -> R {
-        (self.recover)(self.value)
-    }
-}
-#[doc(hidden)]
-pub trait Restore<R> {
-    fn restore(&mut self, recovery: R);
-}
-impl<S> Restore<()> for S {
-    #[inline(always)]
-    fn restore(&mut self, _: ()) {}
-}
-impl<'a> Restore<crate::MutationView<'a>> for MutationSource<'a> {
-    #[inline(always)]
-    fn restore(&mut self, recovery: crate::MutationView<'a>) {
-        debug_assert!(self.0.is_none());
-        self.0 = Some(recovery);
-    }
-}
-
-/// Type-directed matching. The source parameter also expresses whether an
-/// aliased result can retain the input lifetime or transfer its view.
+/// Type-directed matching. A matched value never consumes the source: a
+/// typed mutation view aliases it, and the macro never touches the source
+/// after an arm accepts, so a rejected guard needs no recovery.
 #[doc(hidden)]
 pub trait MatchPattern<S>: Sized {
-    type Recovery;
-    #[allow(clippy::result_unit_err)]
-    fn try_match(source: S) -> Result<Matched<Self, Self::Recovery>, ()>;
+    fn try_match(source: S) -> Option<Self>;
 }
 impl<'b, T: crate::type_traits::ContainerElement, S: SourceView> 
MatchPattern<&'b mut S> for T {
-    type Recovery = ();
     #[inline(always)]
-    fn try_match(source: &'b mut S) -> Result<Matched<Self, ()>, ()> {
+    fn try_match(source: &'b mut S) -> Option<Self> {
         let view = source.view();
         let data = view.as_raw_ffi_any();
         unsafe {
-            if T::container_check_any_strict(data) && 
T::container_match_constraints(data) {
-                Ok(Matched {
-                    value: T::container_copy_from_any_view_after_check(data),
-                    recover: drop,
-                })
-            } else {
-                Err(())
-            }
+            (T::container_check_any_strict(data) && 
T::container_match_constraints(data))
+                .then(|| T::container_copy_from_any_view_after_check(data))
         }
     }
 }
@@ -191,58 +135,25 @@ impl<'a, 'b, 'out> MatchPattern<&'b mut ViewSource<'a>> 
for AnyView<'out>
 where
     'a: 'out,
 {
-    type Recovery = ();
-    #[inline(always)]
-    fn try_match(source: &'b mut ViewSource<'a>) -> Result<Matched<Self, ()>, 
()> {
-        Ok(Matched {
-            value: source.0,
-            recover: drop,
-        })
-    }
-}
-impl<'b> MatchPattern<&'b mut OwnedSource> for AnyView<'b> {
-    type Recovery = ();
     #[inline(always)]
-    fn try_match(source: &'b mut OwnedSource) -> Result<Matched<Self, ()>, ()> 
{
-        Ok(Matched {
-            value: source.view(),
-            recover: drop,
-        })
+    fn try_match(source: &'b mut ViewSource<'a>) -> Option<Self> {
+        Some(source.0)
     }
 }
 impl<'a, 'b> MatchPattern<&'b mut MutationSource<'a>> for AnyView<'b> {
-    type Recovery = ();
     #[inline(always)]
-    fn try_match(source: &'b mut MutationSource<'a>) -> Result<Matched<Self, 
()>, ()> {
-        Ok(Matched {
-            value: source.view(),
-            recover: drop,
-        })
+    fn try_match(source: &'b mut MutationSource<'a>) -> Option<Self> {
+        Some(source.view())
     }
 }
 impl<'a, 'b, T: crate::type_traits::ContainerElement> MatchPattern<&'b mut 
MutationSource<'a>>
     for crate::MutationView<'a, T>
 {
-    type Recovery = crate::MutationView<'a>;
     #[inline(always)]
-    fn try_match(source: &'b mut MutationSource<'a>) -> Result<Matched<Self, 
Self::Recovery>, ()> {
-        match source.0.take().unwrap().downcast::<T>() {
-            Ok(value) => {
-                if unsafe { 
T::container_match_constraints(value.as_any_view().as_raw_ffi_any()) } {
-                    Ok(Matched {
-                        value,
-                        recover: crate::MutationView::into_untyped,
-                    })
-                } else {
-                    source.0 = Some(value.into_untyped());
-                    Err(())
-                }
-            }
-            Err(value) => {
-                source.0 = Some(value);
-                Err(())
-            }
-        }
+    fn try_match(source: &'b mut MutationSource<'a>) -> Option<Self> {
+        // SAFETY: the macro-private source is never used after an arm accepts
+        // this view, so the alias stays the only live handle.
+        unsafe { source.0.alias() }.downcast::<T>().ok()
     }
 }
 
@@ -266,46 +177,36 @@ impl<T: crate::object::ObjectCore> RefPatternMetadata for 
&&PatternConversionPro
     type LeafPattern = BorrowedLeaf<T>;
 }
 impl<T> RefPatternMetadata for &PatternConversionProbe<T> {
-    type LeafPattern = StrictLeaf<T>;
+    type LeafPattern = T;
 }
 
 #[doc(hidden)]
 pub trait RefPatternConversion<'b, S, T> {
     type Holder: std::borrow::Borrow<T>;
-    type Recovery;
-    #[allow(clippy::result_unit_err)]
-    fn try_convert_ref(
-        &self,
-        source: &'b mut S,
-    ) -> Result<Matched<Self::Holder, Self::Recovery>, ()>;
+    fn try_convert_ref(&self, source: &'b mut S) -> Option<Self::Holder>;
 }
 impl<'b, T: crate::type_traits::DowncastRefFromAny + 'static, S: SourceView>
     RefPatternConversion<'b, S, T> for &&PatternConversionProbe<T>
 {
     type Holder = &'b T;
-    type Recovery = ();
     #[inline(always)]
-    fn try_convert_ref(&self, source: &'b mut S) -> 
Result<Matched<Self::Holder, ()>, ()> {
+    fn try_convert_ref(&self, source: &'b mut S) -> Option<Self::Holder> {
         // Borrow the source's own cell, which lives for 'b: a reference type
         // such as `&Add` points into the cell, an object type into the heap.
-        let node = unsafe { T::downcast_ref_from_any(source.cell()) 
}.ok_or(())? as *const T;
+        let node = unsafe { T::downcast_ref_from_any(source.cell()) }? as 
*const T;
         // SAFETY: the checked value is borrowed from source for 'b, and the 
arm
         // does not modify a list or dict holding it (the `match_any!`
         // contract). No owning conversion or view transfer can occur while 
this
         // borrow lives.
-        Ok(Matched {
-            value: unsafe { &*node },
-            recover: drop,
-        })
+        Some(unsafe { &*node })
     }
 }
 impl<'b, S: 'b, T: MatchPattern<&'b mut S>> RefPatternConversion<'b, S, T>
     for &PatternConversionProbe<T>
 {
     type Holder = T;
-    type Recovery = T::Recovery;
     #[inline(always)]
-    fn try_convert_ref(&self, source: &'b mut S) -> Result<Matched<T, 
Self::Recovery>, ()> {
+    fn try_convert_ref(&self, source: &'b mut S) -> Option<T> {
         T::try_match(source)
     }
 }
@@ -442,50 +343,19 @@ where
     }
 }
 
-/// Strict matching metadata is independent of coercive conversion metadata.
-#[doc(hidden)]
-pub struct StrictLeaf<T>(PhantomData<fn() -> T>);
-
-impl<Head, Tail> LeafPatternList for (StrictLeaf<Head>, Tail)
+/// A typed mutation view arm routes as its value type.
+impl<'a, Head, Tail> LeafPatternList for (crate::MutationView<'a, Head>, Tail)
 where
     Head: AnyCompatible + crate::object::ObjectRefCore,
     Tail: LeafPatternList,
 {
-    type Key = (
-        <Head as crate::object::ObjectRefCore>::ContainerType,
-        Tail::Key,
-    );
-    const ALL_EXACT_LEAF: bool = Head::MATCH_ANY_STRICT_EXACT
-        && <Head::ContainerType as crate::object::ObjectCore>::TYPE_FINAL
-        && Tail::ALL_EXACT_LEAF;
-
-    #[inline(always)]
+    type Key = <(Head, Tail) as LeafPatternList>::Key;
+    const ALL_EXACT_LEAF: bool = <(Head, Tail) as 
LeafPatternList>::ALL_EXACT_LEAF;
     fn matches_key() -> bool {
-        Head::match_any_exact_type_index()
-            == <Head::ContainerType as crate::object::ObjectCore>::type_index()
-            && Tail::matches_key()
+        <(Head, Tail) as LeafPatternList>::matches_key()
     }
-
-    #[inline(always)]
     fn fill_leaf_type_indices(out: &mut [i32]) {
-        let (head, tail) = out.split_first_mut().expect("missing leaf pattern 
slot");
-        *head = Head::match_any_exact_type_index();
-        Tail::fill_leaf_type_indices(tail);
-    }
-}
-
-impl<'a, Head, Tail> LeafPatternList for (StrictLeaf<crate::MutationView<'a, 
Head>>, Tail)
-where
-    Head: AnyCompatible + crate::object::ObjectRefCore,
-    Tail: LeafPatternList,
-{
-    type Key = <(StrictLeaf<Head>, Tail) as LeafPatternList>::Key;
-    const ALL_EXACT_LEAF: bool = <(StrictLeaf<Head>, Tail) as 
LeafPatternList>::ALL_EXACT_LEAF;
-    fn matches_key() -> bool {
-        <(StrictLeaf<Head>, Tail) as LeafPatternList>::matches_key()
-    }
-    fn fill_leaf_type_indices(out: &mut [i32]) {
-        <(StrictLeaf<Head>, Tail) as 
LeafPatternList>::fill_leaf_type_indices(out);
+        <(Head, Tail) as LeafPatternList>::fill_leaf_type_indices(out);
     }
 }
 
@@ -536,6 +406,11 @@ pub trait LeafPatternMetadata {
         None
     }
 
+    #[doc(hidden)]
+    fn matches_key(&self) -> bool {
+        false
+    }
+
     #[doc(hidden)]
     fn fill_leaf_type_indices(&self, _out: &mut [i32]) {}
 }
@@ -545,7 +420,12 @@ impl<T> LeafPatternMetadata for &LeafPatternProbe<T> {}
 impl<T: LeafPatternList> LeafPatternMetadata for LeafPatternProbe<T> {
     #[inline(always)]
     fn leaf_pattern_list_id(&self) -> Option<TypeId> {
-        (T::ALL_EXACT_LEAF && T::matches_key()).then(TypeId::of::<T::Key>)
+        T::ALL_EXACT_LEAF.then(TypeId::of::<T::Key>)
+    }
+
+    #[inline(always)]
+    fn matches_key(&self) -> bool {
+        T::matches_key()
     }
 
     #[inline(always)]
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index c9d4a3ff..10c52b3e 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -728,7 +728,6 @@ unsafe impl<T: ObjectRefAnyCompatible> AnyCompatible for T {
     // An overridden hook makes the type inexact; a non-final object type 
always is.
     const MATCH_ANY_EXACT: bool = <T as 
ObjectRefAnyCompatible>::MATCH_ANY_EXACT
         && <T::ContainerType as ObjectCore>::TYPE_FINAL;
-    const MATCH_ANY_STRICT_EXACT: bool = <T as AnyCompatible>::MATCH_ANY_EXACT;
 
     #[inline]
     fn match_any_exact_type_index() -> i32 {
diff --git a/rust/tvm-ffi/src/type_traits.rs b/rust/tvm-ffi/src/type_traits.rs
index ad7fef78..bdcff898 100644
--- a/rust/tvm-ffi/src/type_traits.rs
+++ b/rust/tvm-ffi/src/type_traits.rs
@@ -56,14 +56,6 @@ pub unsafe trait AnyCompatible: Sized {
     #[doc(hidden)]
     const MATCH_ANY_EXACT: bool = false;
 
-    /// Whether strict matching accepts exactly `match_any_exact_type_index()`.
-    /// Unlike `MATCH_ANY_EXACT`, this covers every tag, including scalars:
-    /// `check_any_strict` and `match_constraints` together must succeed if and
-    /// only if the index matches.
-    /// Container-element checks must have the same acceptance set.
-    #[doc(hidden)]
-    const MATCH_ANY_STRICT_EXACT: bool = false;
-
     /// Return the sole runtime object type index accepted by this conversion.
     #[doc(hidden)]
     fn match_any_exact_type_index() -> i32 {
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs 
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index ae84991e..cd2fb8a0 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -2637,8 +2637,8 @@ fn dispatch_handlers_may_return_typed_results() {
             &mut self,
             value: MutationView<'_, Array<i64>>,
             mutator: &mut StructuralMutatorView<'_, Self>,
-        ) -> Result<UnchangedOr<Array<i64>>> {
-            mutator.default_mutate(self, value)?.try_cast()
+        ) -> Result<UnchangedOr<Any>> {
+            mutator.default_mutate(self, value)
         }
     }
     let mutated = structural_mutate(Array::from_vec(vec![1i64, 2]), Typed)

Reply via email to