This is an automated email from the ASF dual-hosted git repository.
tlopex 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 a609ff54 [REFACTOR][Rust] Consolidate structural traversal APIs (#801)
a609ff54 is described below
commit a609ff5493d40a6e85e6f0aa7c8a91b40e9505ab
Author: Shushi Hong <[email protected]>
AuthorDate: Sat Sep 19 15:12:13 2026 -0400
[REFACTOR][Rust] Consolidate structural traversal APIs (#801)
This PR consolidates the Rust structural traversal APIs, uses
`MutateContext` for callbacks and policies and `StructuralView` for
borrowed callback values, and removes redundant aliases and helpers. It
unifies policy traversal through `structural_walk` and `structural_map`,
keeps `def_region_kind()` as the region getter, and renames the default
recursion hook to `on_default_mutate`. It also fixes post-order mapping
so `Unchanged` preserves child rewrites and moves policy compile-fail
coverage into inline doctests. Follow-up to
[[#800](https://github.com/apache/tvm-ffi/pull/800)](https://github.com/apache/tvm-ffi/pull/800).
---
docs/guides/rust_lang_guide.md | 24 +-
rust/tvm-ffi-macros/src/dispatch.rs | 52 +---
rust/tvm-ffi/src/extra/dispatch.rs | 8 +-
rust/tvm-ffi/src/extra/structural_common.rs | 1 -
rust/tvm-ffi/src/extra/structural_mutate.rs | 312 ++++++---------------
rust/tvm-ffi/src/extra/structural_mutate/policy.rs | 27 +-
.../extra/structural_mutate/policy/compile_fail.rs | 50 ----
rust/tvm-ffi/src/extra/structural_visit.rs | 152 +++++-----
rust/tvm-ffi/src/extra/structural_visit/policy.rs | 27 +-
rust/tvm-ffi/src/lib.rs | 10 +-
rust/tvm-ffi/tests/test_structural_mutate.rs | 237 +++++++++-------
rust/tvm-ffi/tests/test_structural_visit.rs | 136 +++++----
.../tests/test_structural_visitor_alignment.rs | 10 +-
13 files changed, 429 insertions(+), 617 deletions(-)
diff --git a/docs/guides/rust_lang_guide.md b/docs/guides/rust_lang_guide.md
index 66d64daa..a4b9709d 100644
--- a/docs/guides/rust_lang_guide.md
+++ b/docs/guides/rust_lang_guide.md
@@ -367,8 +367,9 @@ callback must call `visit_children()` explicitly, and
interrupt values must be
returned explicitly because `?` only propagates errors.
`VisitCallbacks::with_policy` and `WalkWithContextPolicy` use
`ContextPolicy<State>`
-to manage context around default recursion. See the `ContextPolicy` API
-documentation for composition and shared state access.
+to manage context around default recursion. Pass `WalkWithContextPolicy` to
+`structural_walk`; pass `&mut walker` to reuse it and inspect its state
afterward.
+See the `ContextPolicy` API documentation for composition and shared state
access.
For a named implementation, `#[dispatch(visit)]` generates
`StructuralVisitor` from `visit_*` methods. Matching handlers own recursion;
@@ -418,7 +419,8 @@ explicitly from a `StructuralVisitor`, or skip it with a
pre-order
`WalkResult::Skip`.
`StructuralView` is the shared borrowed callback value for visit, walk, map,
-and mutate; `VisitValue` and `MapValue` remain compatibility names.
+and mutate. Mapping callbacks return replacement values; their input view is
+borrowed and does not grant in-place mutation permission.
### Structural Mapping and Mutation
@@ -491,7 +493,8 @@ the same semantics as C++.
`MutateCallbacks::with_policy` and `MapWithContextPolicy` use
`MutContextPolicy<State>`
to manage context around default recursion. Policies consume `MutateValue`
and return `UnchangedOr<Any>`; see the API documentation for composition and
-scoped definition regions. Pass `MapWithContextPolicy` directly to
`structural_map`;
+scoped definition regions. Pass `MapWithContextPolicy` directly to
`structural_map`,
+or pass `&mut mapper` to reuse it and inspect its state afterward;
it cannot be a callback tuple member or another wrapper's dispatcher. Compose
policies as `(outer, inner)` within one wrapper.
@@ -510,18 +513,19 @@ not returned on error.
Callbacks can also return `Unchanged` or `UnchangedOr<T>`, optionally wrapped
in `Result`. A pre-order map still maps the original value's children when
-a callback returns `Unchanged`. Use `mutate_result` and
+a callback returns `Unchanged`; a post-order map keeps the result of child
+mapping. Use `mutate_result` and
`default_mutate_result` to preserve unchanged during recursion; existing
owning-value helpers and top-level functions resolve the marker to the
original value.
`structural_mutate` accepts typed callback chains in addition to a
-`StructuralMutator`. Closure callbacks receive a `CallbackMutator`;
+`StructuralMutator`. Closure callbacks receive a `MutateContext`;
`MutateCallbacks` adds state shared by that callback chain:
```rust
use tvm_ffi::{
- structural_mutate, Array, CallbackMutator, MutateValue, MutateCallbacks,
+ structural_mutate, Array, MutateCallbacks, MutateContext, MutateValue,
};
#[derive(Default)]
@@ -532,11 +536,11 @@ struct Stats {
let mut mutator = MutateCallbacks::new(
Stats::default(),
(
- |value: i64, mutator: &mut CallbackMutator<Stats>| {
+ |value: i64, mutator: &mut MutateContext<'_, Stats>| {
mutator.state_mut().integers += 1;
value + 1
},
- |value: MutateValue<'_>, mutator: &mut CallbackMutator<Stats>| {
+ |value: MutateValue<'_>, mutator: &mut MutateContext<'_, Stats>| {
mutator.default_maybe_inplace_mutate(value)
},
),
@@ -547,7 +551,7 @@ assert_eq!(mutated.iter().collect::<Vec<_>>(), vec![2, 3]);
assert_eq!(mutator.state().integers, 2);
```
-`CallbackMutator::mutate` uses the copy path for a borrowed value, while
+`MutateContext::mutate` uses the copy path for a borrowed value, while
`maybe_inplace_mutate` preserves the reuse opportunity of an owned value.
Closure callbacks are `Fn`; mutable data belongs in the callback state.
diff --git a/rust/tvm-ffi-macros/src/dispatch.rs
b/rust/tvm-ffi-macros/src/dispatch.rs
index feffa55b..b4528d10 100644
--- a/rust/tvm-ffi-macros/src/dispatch.rs
+++ b/rust/tvm-ffi-macros/src/dispatch.rs
@@ -155,10 +155,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
DispatchMode::Visit => quote! {
#tvm_ffi::extra::structural_visit::IntoVisitResult::into_visit_result
},
- DispatchMode::Map => quote! {
- #tvm_ffi::extra::structural_mutate::IntoMapResult::into_map_result
- },
- DispatchMode::Mutate => quote! {
+ DispatchMode::Map | DispatchMode::Mutate => quote! {
#tvm_ffi::extra::structural_mutate::IntoMutateResult::into_mutate_result
},
};
@@ -198,7 +195,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
&mut self,
value: &#tvm_ffi::StructuralView,
def_region_kind:
#tvm_ffi::extra::structural_visit::DefRegionKind,
- ) ->
Option<#tvm_ffi::extra::structural_visit::WalkCallbackResult> {
+ ) -> Option<#tvm_ffi::Result<#tvm_ffi::WalkResult>> {
#(#links)*
None
}
@@ -232,7 +229,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
&mut self,
value: &#tvm_ffi::StructuralView,
def_region_kind:
#tvm_ffi::extra::structural_visit::DefRegionKind,
- ) -> Option<#tvm_ffi::extra::structural_mutate::MapResult> {
+ ) -> Option<#tvm_ffi::Result<#tvm_ffi::Any>> {
#(#links)*
None
}
@@ -248,7 +245,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
&mut self,
value: &#tvm_ffi::StructuralView,
mutator: &mut #tvm_ffi::extra::structural_mutate::Mutator,
- ) -> Option<#tvm_ffi::extra::structural_mutate::MutateResult> {
+ ) -> Option<#tvm_ffi::Result<#tvm_ffi::Any>> {
<Self as
#tvm_ffi::extra::structural_mutate::MutateDispatch>::dispatch_mutate_value(
self, #tvm_ffi::MutateValue::borrowed(value), mutator,
)
@@ -260,8 +257,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
&mut self,
mut value: #tvm_ffi::MutateValue<'_>,
mutator: &mut #tvm_ffi::Mutator,
- ) -> Option<#tvm_ffi::extra::structural_mutate::MutateResult> {
- let inplace_mode = value.inplace_mode();
+ ) -> Option<#tvm_ffi::Result<#tvm_ffi::Any>> {
#(#links)*
None
}
@@ -289,9 +285,6 @@ fn expand_links(
let method = &handler.method;
let attrs = &handler.cfg_attrs;
let trailing_arg = match mode {
- DispatchMode::Mutate if handler.wants_inplace_mode => {
- quote!(, mutator, inplace_mode)
- }
DispatchMode::Mutate if handler.wants_mutator => quote!(,
mutator),
DispatchMode::Mutate => quote!(),
_ if handler.wants_def_region => quote!(, def_region_kind),
@@ -365,7 +358,6 @@ struct Handler {
argument: HandlerArgument,
wants_def_region: bool,
wants_mutator: bool,
- wants_inplace_mode: bool,
cfg_attrs: Vec<Meta>,
}
@@ -378,21 +370,16 @@ enum HandlerArgument {
fn parse_handler(method: &ImplItemMethod, mode: DispatchMode) ->
syn::Result<Handler> {
let inputs = &method.sig.inputs;
- let receiver_is_expected = match (mode, inputs.first()) {
- (DispatchMode::Mutate, Some(FnArg::Receiver(receiver))) => {
- receiver.reference.is_some() && receiver.mutability.is_some()
- }
- (_, Some(FnArg::Receiver(receiver))) => {
+ let receiver_is_expected = match inputs.first() {
+ Some(FnArg::Receiver(receiver)) => {
receiver.reference.is_some() && receiver.mutability.is_some()
}
_ => false,
};
- let arity_is_expected = inputs.len() == 2
- || inputs.len() == 3
- || (matches!(mode, DispatchMode::Mutate) && inputs.len() == 4);
+ let arity_is_expected = inputs.len() == 2 || inputs.len() == 3;
if !receiver_is_expected || !arity_is_expected {
let message = if matches!(mode, DispatchMode::Mutate) {
- "mutate handlers must take `&mut self`, a node, optionally `&mut
Mutator`, then optionally `InplaceMode`"
+ "mutate handlers must take `&mut self`, a node, and optionally
`&mut Mutator`"
.to_owned()
} else {
format!(
@@ -405,16 +392,6 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
}
let wants_def_region = !matches!(mode, DispatchMode::Mutate) &&
inputs.len() == 3;
let wants_mutator = matches!(mode, DispatchMode::Mutate) && inputs.len()
>= 3;
- let wants_inplace_mode = matches!(mode, DispatchMode::Mutate) &&
inputs.len() == 4;
- if wants_inplace_mode {
- let Some(FnArg::Typed(arg)) = inputs.iter().nth(3) else {
- unreachable!()
- };
- if !matches!(arg.ty.as_ref(), Type::Path(path) if
path.path.segments.last().is_some_and(|s| s.ident == "InplaceMode" &&
matches!(s.arguments, PathArguments::None)))
- {
- return Err(syn::Error::new_spanned(&arg.ty, "expected
`InplaceMode`"));
- }
- }
if wants_mutator {
let context_type = match inputs.iter().nth(2) {
Some(FnArg::Typed(context)) => context.ty.as_ref(),
@@ -474,7 +451,6 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
argument,
wants_def_region,
wants_mutator,
- wants_inplace_mode,
cfg_attrs,
})
}
@@ -562,10 +538,8 @@ fn is_dispatch_value(value_type: &Type) -> bool {
let Type::Path(path) = value_type else {
return false;
};
- path.path.segments.last().is_some_and(|segment| {
- matches!(
- segment.ident.to_string().as_str(),
- "StructuralView" | "VisitValue" | "MapValue"
- )
- })
+ path.path
+ .segments
+ .last()
+ .is_some_and(|segment| segment.ident == "StructuralView")
}
diff --git a/rust/tvm-ffi/src/extra/dispatch.rs
b/rust/tvm-ffi/src/extra/dispatch.rs
index af6f2a8e..42b263b4 100644
--- a/rust/tvm-ffi/src/extra/dispatch.rs
+++ b/rust/tvm-ffi/src/extra/dispatch.rs
@@ -21,9 +21,7 @@
use crate::error::Result;
-use super::structural_visit::{
- DefRegionKind, IntoWalker, NativeVisit, StructuralView,
WalkCallbackResult, WalkResult,
-};
+use super::structural_visit::{DefRegionKind, IntoWalker, NativeVisit,
StructuralView, WalkResult};
/// Dispatch for typed `structural_walk` observer callbacks.
///
@@ -34,7 +32,7 @@ pub trait WalkDispatch: Sized {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult>;
+ ) -> Option<Result<WalkResult>>;
}
impl<V: WalkDispatch> WalkDispatch for &mut V {
@@ -43,7 +41,7 @@ impl<V: WalkDispatch> WalkDispatch for &mut V {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
(**self).dispatch_walk(value, def_region_kind)
}
}
diff --git a/rust/tvm-ffi/src/extra/structural_common.rs
b/rust/tvm-ffi/src/extra/structural_common.rs
index 73242c84..3447692f 100644
--- a/rust/tvm-ffi/src/extra/structural_common.rs
+++ b/rust/tvm-ffi/src/extra/structural_common.rs
@@ -70,7 +70,6 @@ pub(crate) use impl_callback_chain_tuple_arities;
/// borrows an object node, while [`Self::cast`] returns a typed value
(acquiring
/// ownership for object handles). This view does not grant in-place
permission;
/// consuming mutation callbacks use [`crate::MutateValue`] instead.
-/// `VisitValue` and `MapValue` remain compatibility names for this type.
#[repr(transparent)]
pub struct StructuralView(TVMFFIAny);
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 04b2cfd5..1f656142 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -68,16 +68,9 @@ const FLAG_SETTER_IS_FUNCTION: i64 =
kTVMFFIFieldFlagBitSetterIsFunctionObj as i
/// Borrowed value passed to structural map and mutation callbacks.
pub use super::StructuralView;
-/// Compatibility name for [`StructuralView`].
-pub use super::StructuralView as MapValue;
-
mod policy;
pub use policy::{DefaultMutContextPolicy, MapWithContextPolicy,
MutContextPolicy};
-/// Result type produced by a structural-map callback.
-#[doc(hidden)]
-pub type MapResult = Result<Any>;
-
mod callback_result_sealed {
use super::{Any, Result};
@@ -87,27 +80,27 @@ mod callback_result_sealed {
impl<T: Into<Any>> Sealed for Result<T> {}
}
-/// Convert an infallible or fallible callback result into [`MapResult`].
+/// Convert an infallible or fallible map or mutation callback result into
[`Result<Any>`].
///
/// A callback may return any value convertible into [`Any`], or wrap it in
/// [`Result`] to use `?`.
///
/// This trait is sealed and is not an extension point.
#[doc(hidden)]
-pub trait IntoMapResult: callback_result_sealed::Sealed {
- fn into_map_result(self) -> MapResult;
+pub trait IntoMutateResult: callback_result_sealed::Sealed {
+ fn into_mutate_result(self) -> Result<Any>;
}
-impl<T: Into<Any>> IntoMapResult for T {
+impl<T: Into<Any>> IntoMutateResult for T {
#[inline]
- fn into_map_result(self) -> MapResult {
+ fn into_mutate_result(self) -> Result<Any> {
Ok(self.into())
}
}
-impl<T: Into<Any>> IntoMapResult for Result<T> {
+impl<T: Into<Any>> IntoMutateResult for Result<T> {
#[inline]
- fn into_map_result(self) -> MapResult {
+ fn into_mutate_result(self) -> Result<Any> {
self.map(Into::into)
}
}
@@ -154,8 +147,8 @@ impl InplaceMode {
///
/// A node borrow cannot survive consumption of the handle:
/// ```compile_fail
-/// use tvm_ffi::{CallbackMutator, MutateValue};
-/// fn invalid(value: MutateValue<'_>, ctx: &mut CallbackMutator) {
+/// use tvm_ffi::{MutateContext, MutateValue};
+/// fn invalid(value: MutateValue<'_>, ctx: &mut MutateContext<'_>) {
/// let node =
value.as_node::<tvm_ffi::collections::array::ArrayObj>().unwrap();
/// ctx.default_maybe_inplace_mutate(value).unwrap();
/// println!("{}", node.size);
@@ -165,11 +158,11 @@ impl InplaceMode {
/// Moving the handle into a nested callback cannot bypass that borrow:
/// ```compile_fail
/// use std::cell::RefCell;
-/// use tvm_ffi::{structural_mutate, CallbackMutator, MutateValue};
+/// use tvm_ffi::{structural_mutate, MutateContext, MutateValue};
/// fn invalid(value: MutateValue<'_>) {
/// let node =
value.as_node::<tvm_ffi::collections::array::ArrayObj>().unwrap();
/// let pending = RefCell::new(Some(value));
-/// structural_mutate(true, |_: bool, inner: &mut CallbackMutator| {
+/// structural_mutate(true, |_: bool, inner: &mut MutateContext<'_>| {
///
inner.default_maybe_inplace_mutate(pending.borrow_mut().take().unwrap())
/// }).unwrap();
/// println!("{}", node.size);
@@ -257,27 +250,19 @@ impl<T> Deref for MutateValue<'_, T> {
}
}
-/// State and recursive operations available to a callback-chain mutation.
+/// State and recursive operations shared by mutation callbacks and context
policies.
///
/// A matched callback owns mutation of its value. Recursive operations
/// reborrow the mutator, so mutable state cannot remain borrowed across them.
/// The context does not store a node: inspect the callback argument and pass
/// it explicitly to default recursion.
-pub struct MutateContext<'a, State, Driver: ?Sized = dyn
MutateContextDriver<State> + 'a> {
- driver: &'a mut Driver,
+pub struct MutateContext<'a, State = ()> {
+ driver: &'a mut dyn MutateContextDriver<State>,
def_region_kind: DefRegionKind,
inplace_mode: InplaceMode,
- _state: PhantomData<fn() -> State>,
_not_send_sync: PhantomData<Rc<()>>,
}
-/// Recursive mutation operations passed to closure callback chains.
-///
-/// Typed `#[dispatch(mutate)]` implementations use [`Mutator`] instead and
-/// keep their mutable pass state directly on the dispatch object.
-pub type CallbackMutator<'a, State = (), Driver = dyn
MutateContextDriver<State> + 'a> =
- MutateContext<'a, State, Driver>;
-
/// Recursion control passed to a typed `#[dispatch(mutate)]` handler.
///
/// The dispatch object owns all pass state. Recursive operations take that
@@ -305,12 +290,6 @@ impl Mutator {
self.def_region_kind
}
- /// Definition region active at the callback's current value.
- #[inline(always)]
- pub fn region(&self) -> DefRegionKind {
- self.def_region_kind
- }
-
/// Mutate a borrowed child through the same typed dispatch object.
#[inline(always)]
pub fn mutate<D, T>(&mut self, dispatch: &mut D, value: &T) -> Result<Any>
@@ -386,7 +365,7 @@ impl Mutator {
D: MutateDispatch,
for<'x> AnyView<'x>: From<&'x T>,
{
- StructuralMutator::default_mutate_value(dispatch, value,
self.def_region_kind)
+ StructuralMutator::default_mutate(dispatch, value,
self.def_region_kind)
}
/// Mutate a borrowed child while preserving an unchanged result.
@@ -425,7 +404,7 @@ impl Mutator {
D: MutateDispatch,
for<'x> AnyView<'x>: From<&'x T>,
{
- StructuralMutator::default_mutate_value_result(dispatch, value,
self.def_region_kind)
+ StructuralMutator::default_mutate_result(dispatch, value,
self.def_region_kind)
}
/// Consume the handle for default descent with its existing permission.
@@ -500,14 +479,11 @@ impl Mutator {
}
}
-#[doc(hidden)]
/// Internal operations used by [`MutateContext`].
///
-/// The dispatch macro keeps the concrete implementor visible to the compiler
-/// so recursive `mutate` calls can be inlined. This is not a user extension
-/// point. Borrowed inputs carry a lifetime; in-place entry requires an owned
+/// Borrowed inputs carry a lifetime; in-place entry requires an owned
/// value or a consumed capability, never a bare ABI value plus permission.
-pub trait MutateContextDriver<State> {
+trait MutateContextDriver<State> {
fn state(&self) -> &State;
fn state_mut(&mut self) -> &mut State;
fn mutate_borrowed(&mut self, value: AnyView<'_>, kind: DefRegionKind) ->
Result<Any>;
@@ -526,10 +502,7 @@ pub trait MutateContextDriver<State> {
fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()>;
}
-impl<State, Driver> MutateContext<'_, State, Driver>
-where
- Driver: MutateContextDriver<State> + ?Sized,
-{
+impl<State> MutateContext<'_, State> {
/// User state shared by every callback in this mutation.
#[inline(always)]
pub fn state(&self) -> &State {
@@ -556,25 +529,18 @@ where
self.def_region_kind
}
- /// Definition region active at the callback's current value.
- #[inline]
- pub fn region(&self) -> DefRegionKind {
- self.def_region_kind
- }
-
/// Run recursive operations in a definition region, preserving an outer
Pattern.
/// The previous context is restored on return, error, or unwinding.
pub fn with_def_region_kind<T>(
&mut self,
kind: DefRegionKind,
- callback: impl FnOnce(&mut MutateContext<'_, State, Driver>) ->
Result<T>,
+ callback: impl FnOnce(&mut MutateContext<'_, State>) -> Result<T>,
) -> Result<T> {
with_mutation_region(kind, |kind| {
callback(&mut MutateContext {
driver: &mut *self.driver,
def_region_kind: kind,
inplace_mode: self.inplace_mode,
- _state: PhantomData,
_not_send_sync: PhantomData,
})
})
@@ -751,7 +717,7 @@ where
/// remains available for closure callback chains with separate state.
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a supported `structural_mutate` mutator",
- note = "accepted mutators: `&mut U` where `U: StructuralMutator`; a
generated `MutateDispatch`; an `Fn` callback over an FFI value type `T`, `&N`
of an object node type, or `&StructuralView`, or a consuming `MutateValue<T>`,
followed by `&mut CallbackMutator<State>`; or a tuple of up to 12 such
callbacks (tuples may nest)",
+ note = "accepted mutators: `&mut U` where `U: StructuralMutator`; a
generated `MutateDispatch`; an `Fn` callback over an FFI value type `T`, `&N`
of an object node type, or `&StructuralView`, or a consuming `MutateValue<T>`,
followed by `&mut MutateContext<'_, State>`; or a tuple of up to 12 such
callbacks (tuples may nest)",
note = "callback arguments need explicit type annotations; use
`MutateCallbacks::new(state, callbacks)` for ordinary mutable callback state"
)]
pub trait IntoMutator<Marker> {
@@ -765,47 +731,14 @@ impl<U: StructuralMutator> IntoMutator<U> for &mut U {
}
}
-/// Convert a mutation callback result into [`Result<Any>`].
-///
-/// A callback may return any value convertible into [`Any`], or wrap it in
-/// [`Result`] to use `?`.
-#[doc(hidden)]
-pub trait IntoMutateResult: callback_result_sealed::Sealed {
- fn into_mutate_result(self) -> Result<Any>;
-}
-
-impl<T: Into<Any>> IntoMutateResult for T {
- #[inline]
- fn into_mutate_result(self) -> Result<Any> {
- Ok(self.into())
- }
-}
-
-impl<T: Into<Any>> IntoMutateResult for Result<T> {
- #[inline]
- fn into_mutate_result(self) -> Result<Any> {
- self.map(Into::into)
- }
-}
-
-#[doc(hidden)]
-pub type MutateResult = Result<Any>;
-
-#[doc(hidden)]
-/// Callback tuples use a type-erased mutation driver.
-pub enum DynamicMutateCallbacks {}
-
/// One typed callback in a callback-driven structural mutator.
pub trait MutateChainLink<State, Marker>: mutate_sealed::SealedLink<State,
Marker> {
- #[doc(hidden)]
- type Strategy;
-
#[doc(hidden)]
fn try_mutate(
&self,
value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
- ) -> Option<MutateResult>;
+ ) -> Option<Result<Any>>;
}
/// Ordered typed callback dispatch for [`structural_mutate`].
@@ -816,13 +749,13 @@ pub trait MutateChainLink<State, Marker>:
mutate_sealed::SealedLink<State, Marke
/// first match. The implementation owns its pass state and receives `&mut
/// self`, while [`Mutator`] controls recursion, the definition region, and
/// in-place permission. Handlers can consume [`MutateValue`] and take an
-/// optional trailing [`InplaceMode`] after `&mut Mutator`.
+/// optional `&mut Mutator` to inspect the region and in-place permission.
pub trait MutateDispatch: Sized {
fn dispatch_mutate(
&mut self,
value: &StructuralView,
mutator: &mut Mutator,
- ) -> Option<MutateResult>;
+ ) -> Option<Result<Any>>;
/// Dispatch an engine-issued value. Existing borrowed dispatchers retain
/// their copy-only default recursion; generated dispatch supports
consuming it.
@@ -830,7 +763,7 @@ pub trait MutateDispatch: Sized {
&mut self,
value: MutateValue<'_>,
mutator: &mut Mutator,
- ) -> Option<MutateResult> {
+ ) -> Option<Result<Any>> {
self.dispatch_mutate(value.as_value(), mutator)
}
}
@@ -900,12 +833,11 @@ where
T: crate::type_traits::ContainerElement,
O: IntoMutateResult,
{
- type Strategy = DynamicMutateCallbacks;
fn try_mutate(
&self,
value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
- ) -> Option<MutateResult> {
+ ) -> Option<Result<Any>> {
match value
.take()
.expect("unconsumed callback value")
@@ -929,13 +861,11 @@ where
T: crate::type_traits::AnyCompatible,
O: IntoMutateResult,
{
- type Strategy = DynamicMutateCallbacks;
-
fn try_mutate(
&self,
value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
- ) -> Option<MutateResult> {
+ ) -> Option<Result<Any>> {
value
.as_ref()
.expect("unconsumed callback value")
@@ -956,13 +886,11 @@ where
N: ObjectCore,
O: IntoMutateResult,
{
- type Strategy = DynamicMutateCallbacks;
-
fn try_mutate(
&self,
value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
- ) -> Option<MutateResult> {
+ ) -> Option<Result<Any>> {
value
.as_ref()
.expect("unconsumed callback value")
@@ -982,13 +910,11 @@ where
) -> O,
O: IntoMutateResult,
{
- type Strategy = DynamicMutateCallbacks;
-
fn try_mutate(
&self,
value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
- ) -> Option<MutateResult> {
+ ) -> Option<Result<Any>> {
Some(
self(
value
@@ -1019,13 +945,11 @@ macro_rules! impl_mutate_chain_link {
where
$($F: MutateChainLink<State, $M>,)+
{
- type Strategy = DynamicMutateCallbacks;
-
fn try_mutate(
&self,
value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
- ) -> Option<MutateResult> {
+ ) -> Option<Result<Any>> {
$(
if let Some(result) = self.$idx.try_mutate(value, mutator)
{
return Some(result);
@@ -1131,7 +1055,6 @@ pub struct
ByMutateCallbacks<Marker>(PhantomData<fn(Marker)>);
impl<Link, Marker> IntoMutator<ByMutateCallbacks<Marker>> for Link
where
Link: MutateChainLink<(), Marker>,
- Link::Strategy: MutateCallbackStrategy<(), Link, Marker>,
{
fn mutate_root(self, root: Any) -> Result<Any> {
let callbacks = self;
@@ -1154,7 +1077,7 @@ pub trait MapDispatch: Sized {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult>;
+ ) -> Option<Result<Any>>;
}
/// Internal root-mapping protocol used by [`IntoMapper`].
@@ -1183,7 +1106,7 @@ impl<V: MapDispatch> MapDispatch for &mut V {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
(**self).dispatch_map(value, def_region_kind)
}
}
@@ -1221,53 +1144,53 @@ pub trait MapChainLink<Marker>:
sealed_map::SealedMapLink<Marker> {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult>;
+ ) -> Option<Result<Any>>;
}
mod sealed_map {
- use super::{DefRegionKind, IntoMapResult, MapDispatch, ObjectCore,
StructuralView};
+ use super::{DefRegionKind, IntoMutateResult, MapDispatch, ObjectCore,
StructuralView};
pub trait SealedMapLink<Marker> {}
impl<F, T, O> SealedMapLink<super::ByMapOwned<T>> for F
where
F: FnMut(T) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
}
impl<F, T, O> SealedMapLink<super::ByMapOwnedKind<T>> for F
where
F: FnMut(T, DefRegionKind) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
}
impl<F, N: ObjectCore, O> SealedMapLink<super::ByMapNode<N>> for F
where
F: for<'a> FnMut(&'a N) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
}
impl<F, N: ObjectCore, O> SealedMapLink<super::ByMapNodeKind<N>> for F
where
F: for<'a> FnMut(&'a N, DefRegionKind) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
}
impl<F, O> SealedMapLink<super::ByMapCatchAll> for F
where
F: for<'a> FnMut(&'a StructuralView) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
}
impl<F, O> SealedMapLink<super::ByMapCatchAllKind> for F
where
F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
}
@@ -1281,15 +1204,17 @@ impl<F, T, O> MapChainLink<ByMapOwned<T>> for F
where
F: FnMut(T) -> O,
T: crate::type_traits::AnyCompatible,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
#[inline]
fn try_map(
&mut self,
value: &StructuralView,
_def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
- value.cast::<T>().map(|typed| self(typed).into_map_result())
+ ) -> Option<Result<Any>> {
+ value
+ .cast::<T>()
+ .map(|typed| self(typed).into_mutate_result())
}
}
@@ -1300,17 +1225,17 @@ impl<F, T, O> MapChainLink<ByMapOwnedKind<T>> for F
where
F: FnMut(T, DefRegionKind) -> O,
T: crate::type_traits::AnyCompatible,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
#[inline]
fn try_map(
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
value
.cast::<T>()
- .map(|typed| self(typed, def_region_kind).into_map_result())
+ .map(|typed| self(typed, def_region_kind).into_mutate_result())
}
}
@@ -1321,17 +1246,17 @@ impl<F, N, O> MapChainLink<ByMapNode<N>> for F
where
F: for<'a> FnMut(&'a N) -> O,
N: ObjectCore,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
#[inline]
fn try_map(
&mut self,
value: &StructuralView,
_def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
value
.as_node::<N>()
- .map(|node| self(node).into_map_result())
+ .map(|node| self(node).into_mutate_result())
}
}
@@ -1342,17 +1267,17 @@ impl<F, N, O> MapChainLink<ByMapNodeKind<N>> for F
where
F: for<'a> FnMut(&'a N, DefRegionKind) -> O,
N: ObjectCore,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
#[inline]
fn try_map(
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
value
.as_node::<N>()
- .map(|node| self(node, def_region_kind).into_map_result())
+ .map(|node| self(node, def_region_kind).into_mutate_result())
}
}
@@ -1362,15 +1287,15 @@ pub enum ByMapCatchAll {}
impl<F, O> MapChainLink<ByMapCatchAll> for F
where
F: for<'a> FnMut(&'a StructuralView) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
#[inline]
fn try_map(
&mut self,
value: &StructuralView,
_def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
- Some(self(value).into_map_result())
+ ) -> Option<Result<Any>> {
+ Some(self(value).into_mutate_result())
}
}
@@ -1380,15 +1305,15 @@ pub enum ByMapCatchAllKind {}
impl<F, O> MapChainLink<ByMapCatchAllKind> for F
where
F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
#[inline]
fn try_map(
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
- Some(self(value, def_region_kind).into_map_result())
+ ) -> Option<Result<Any>> {
+ Some(self(value, def_region_kind).into_mutate_result())
}
}
@@ -1404,7 +1329,7 @@ impl<V: MapDispatch> MapChainLink<ByMapDispatchLink> for
&mut V {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
self.dispatch_map(value, def_region_kind)
}
}
@@ -1435,7 +1360,7 @@ where
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
self.link.try_map(value, def_region_kind)
}
}
@@ -1457,7 +1382,7 @@ macro_rules! impl_map_chain_link {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<MapResult> {
+ ) -> Option<Result<Any>> {
$(
if let Some(result) = self.$idx.try_map(value,
def_region_kind) {
return Some(result);
@@ -1490,7 +1415,7 @@ macro_rules! impl_bare_map_link {
where
F: FnMut($($fn_args),+) -> O,
Self: MapChainLink<$marker<T>>,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
type Mapper = MapChain<F, $marker<T>>;
@@ -1513,7 +1438,7 @@ impl_bare_map_link!(
impl<F, O> IntoMapper<ByMapCatchAll> for F
where
F: for<'a> FnMut(&'a StructuralView) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
type Mapper = MapChain<F, ByMapCatchAll>;
@@ -1526,7 +1451,7 @@ where
impl<F, O> IntoMapper<ByMapCatchAllKind> for F
where
F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
- O: IntoMapResult,
+ O: IntoMutateResult,
{
type Mapper = MapChain<F, ByMapCatchAllKind>;
@@ -1641,11 +1566,7 @@ pub trait StructuralMutator: Sized {
}
#[doc(hidden)]
- fn dispatch_default_mutate(
- &mut self,
- value: MutateValue<'_>,
- kind: DefRegionKind,
- ) -> Result<Any> {
+ fn on_default_mutate(&mut self, value: MutateValue<'_>, kind:
DefRegionKind) -> Result<Any> {
default_mutate_driver(
self,
value.value.raw(),
@@ -1698,23 +1619,13 @@ pub trait StructuralMutator: Sized {
Ok(if is_unchanged(&result) { value } else { result })
}
- /// Apply default non-in-place mutation to `value`'s children.
- fn default_mutate(
- &mut self,
- value: &StructuralView,
- def_region_kind: DefRegionKind,
- ) -> Result<Any> {
- user_default_mutate(self, value.raw(), def_region_kind, Permit::Copy)
- .and_then(|result| resolve_result(result, value.raw()))
- }
-
/// 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>
+ fn default_mutate<T>(&mut self, value: &T, def_region_kind: DefRegionKind)
-> Result<Any>
where
for<'x> AnyView<'x>: From<&'x T>,
{
@@ -1764,18 +1675,8 @@ pub trait StructuralMutator: Sized {
.and_then(UnchangedOr::from_carrier)
}
- /// Default non-in-place mutation with an unchanged-or-replacement result.
- fn default_mutate_result(
- &mut self,
- value: &StructuralView,
- kind: DefRegionKind,
- ) -> Result<UnchangedOr<Any>> {
- user_default_mutate(self, value.raw(), kind, Permit::Copy)
- .and_then(UnchangedOr::from_carrier)
- }
-
/// Default mutation of a borrowed typed value, preserving unchanged.
- fn default_mutate_value_result<T>(
+ fn default_mutate_result<T>(
&mut self,
value: &T,
kind: DefRegionKind,
@@ -1876,51 +1777,6 @@ impl<D: MutateDispatch> StructuralMutator for D {
// Closure callback chains use a type-erased context driver so one concrete
// function signature can recurse through the complete chain.
-trait MutateCallbackStrategy<State, Link, Marker> {
- fn try_mutate<Driver>(
- driver: &mut Driver,
- callback_ptr: *const Link,
- value: &StructuralView,
- def_region_kind: DefRegionKind,
- inplace_mode: InplaceMode,
- ) -> Option<MutateResult>
- where
- Driver: MutateContextDriver<State>;
-}
-
-impl<State, Link, Marker> MutateCallbackStrategy<State, Link, Marker> for
DynamicMutateCallbacks
-where
- Link: MutateChainLink<State, Marker>,
-{
- #[inline(always)]
- fn try_mutate<Driver>(
- driver: &mut Driver,
- callback_ptr: *const Link,
- value: &StructuralView,
- def_region_kind: DefRegionKind,
- inplace_mode: InplaceMode,
- ) -> Option<MutateResult>
- where
- Driver: MutateContextDriver<State>,
- {
- let mut mutator = MutateContext::<State, dyn
MutateContextDriver<State>> {
- driver,
- def_region_kind,
- inplace_mode,
- _state: PhantomData,
- _not_send_sync: PhantomData,
- };
- // SAFETY: The owning `Rc` or the direct callback's stack slot remains
live
- // and is never modified through the driver during recursive reentry.
- unsafe {
- (&*callback_ptr).try_mutate(
- &mut Some(MutateValue::new(value, inplace_mode)),
- &mut mutator,
- )
- }
- }
-}
-
#[inline(always)]
fn try_mutate_callbacks<State, Link, Marker, Driver>(
driver: &mut Driver,
@@ -1928,26 +1784,31 @@ fn try_mutate_callbacks<State, Link, Marker, Driver>(
value: &StructuralView,
def_region_kind: DefRegionKind,
inplace_mode: InplaceMode,
-) -> Option<MutateResult>
+) -> Option<Result<Any>>
where
Link: MutateChainLink<State, Marker>,
- Link::Strategy: MutateCallbackStrategy<State, Link, Marker>,
Driver: MutateContextDriver<State>,
{
- <Link::Strategy as MutateCallbackStrategy<State, Link,
Marker>>::try_mutate(
+ let mut mutator = MutateContext {
driver,
- callback_ptr,
- value,
def_region_kind,
inplace_mode,
- )
+ _not_send_sync: PhantomData,
+ };
+ // SAFETY: The owning `Rc` or the direct callback's stack slot remains live
+ // and is never modified through the driver during recursive reentry.
+ unsafe {
+ (&*callback_ptr).try_mutate(
+ &mut Some(MutateValue::new(value, inplace_mode)),
+ &mut mutator,
+ )
+ }
}
impl<State, Link, Marker, Policy> StructuralMutator for MutateCallbacks<State,
Link, Marker, Policy>
where
Policy: MutContextPolicy<State>,
Link: MutateChainLink<State, Marker>,
- Link::Strategy: MutateCallbackStrategy<State, Link, Marker>,
{
#[inline(always)]
fn dispatch_mutate(
@@ -1989,11 +1850,7 @@ where
}
}
- fn dispatch_default_mutate(
- &mut self,
- value: MutateValue<'_>,
- kind: DefRegionKind,
- ) -> Result<Any> {
+ fn on_default_mutate(&mut self, value: MutateValue<'_>, kind:
DefRegionKind) -> Result<Any> {
match self.policy.clone() {
Some(policy) => policy::mutate_with_policy(
&mut policy::MutationDescent { driver: self },
@@ -2014,7 +1871,6 @@ where
impl<Link, Marker> StructuralMutator for DirectMutateCallbacks<'_, Link,
Marker>
where
Link: MutateChainLink<(), Marker>,
- Link::Strategy: MutateCallbackStrategy<(), Link, Marker>,
{
#[inline(always)]
fn dispatch_mutate(
@@ -2218,7 +2074,9 @@ impl<D: MapDispatch, Policy: MutContextPolicy<D>>
NativeMapper<'_, D, Policy> {
*mapped.as_raw_ffi_any()
};
let value = StructuralView::from_raw(mapped_raw);
+ // Keep child rewrites when the callback leaves its input
unchanged.
match self.dispatch.dispatch_map(&value, def_region_kind) {
+ Some(Ok(result)) if is_unchanged(&result) => Ok(mapped),
Some(result) => result,
None => Ok(mapped),
}
@@ -3118,7 +2976,7 @@ fn user_default_mutate<U: StructuralMutator>(
) -> Result<Any> {
with_mutation_region(def_region_kind, |kind| {
let value = StructuralView::from_raw(raw);
- mutator.dispatch_default_mutate(MutateValue::new(&value,
permit.inplace_mode(raw)), kind)
+ mutator.on_default_mutate(MutateValue::new(&value,
permit.inplace_mode(raw)), kind)
})
}
diff --git a/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
b/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
index 1ebe7a0d..1d9cdd97 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
@@ -21,9 +21,6 @@
use super::*;
-#[cfg(doctest)]
-mod compile_fail;
-
/// Default-recursion policy for [`MutateCallbacks::with_policy`] and
[`MapWithContextPolicy`].
///
/// `ctx.default_maybe_inplace_mutate_result(value)` continues to the next
policy,
@@ -86,7 +83,6 @@ pub(super) fn mutate_with_policy<State>(
driver,
def_region_kind: kind,
inplace_mode: value.inplace_mode(),
- _state: PhantomData,
_not_send_sync: PhantomData,
};
policy.default_mutate(value, &mut ctx).map(Any::from)
@@ -195,8 +191,8 @@ impl<D, Policy> MutateCallbackState<D> for NativeMapper<'_,
D, Policy> {
/// A [`MapDispatch`] with a [`MutContextPolicy`], sharing the dispatcher's
state.
///
-/// Run with [`Self::map`] or [`structural_map`]. Each node's map callback runs
-/// outside its policy scope; its children run inside.
+/// Pass this value or a mutable reference to [`structural_map`]. Each node's
+/// map callback runs outside its policy scope; its children run inside.
/// This mapper cannot be a callback tuple member or another wrapper's
dispatcher.
/// Compose policies as `(outer, inner)` within one wrapper.
pub struct MapWithContextPolicy<Mapper, Policy> {
@@ -227,11 +223,14 @@ impl<Mapper: MapDispatch, Policy:
MutContextPolicy<Mapper>> MapWithContextPolicy
pub fn into_state(self) -> Mapper {
self.mapper
}
+}
- /// Map an owning root with fresh invocation-local identity substitutions.
- pub fn map<R: Into<Any>>(&mut self, root: R, order: WalkOrder) ->
Result<Any> {
+impl<Mapper: MapDispatch, Policy: MutContextPolicy<Mapper>> NativeMap
+ for MapWithContextPolicy<Mapper, Policy>
+{
+ fn map_root(&mut self, root: Any, order: WalkOrder) -> Result<Any> {
run_structural_mutator(
- root.into(),
+ root,
&mut NativeMapper {
dispatch: &mut self.mapper,
order,
@@ -242,19 +241,11 @@ impl<Mapper: MapDispatch, Policy:
MutContextPolicy<Mapper>> MapWithContextPolicy
}
}
-impl<Mapper: MapDispatch, Policy: MutContextPolicy<Mapper>> NativeMap
- for MapWithContextPolicy<Mapper, Policy>
-{
- fn map_root(&mut self, root: Any, order: WalkOrder) -> Result<Any> {
- self.map(root, order)
- }
-}
-
impl<Mapper: MapDispatch, Policy: MutContextPolicy<Mapper>> NativeMap
for &mut MapWithContextPolicy<Mapper, Policy>
{
fn map_root(&mut self, root: Any, order: WalkOrder) -> Result<Any> {
- self.map(root, order)
+ (**self).map_root(root, order)
}
}
diff --git a/rust/tvm-ffi/src/extra/structural_mutate/policy/compile_fail.rs
b/rust/tvm-ffi/src/extra/structural_mutate/policy/compile_fail.rs
deleted file mode 100644
index 16bb79be..00000000
--- a/rust/tvm-ffi/src/extra/structural_mutate/policy/compile_fail.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-//! A policy mapper cannot be used as a callback, even in a one-element tuple.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: &mut MapWithContextPolicy<D,
DefaultMutContextPolicy>) {
-//! structural_map(1_i64, (mapper,), WalkOrder::PreOrder).unwrap();
-//! }
-//! ```
-//!
-//! Adding another callback must not discard the policy.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: &mut MapWithContextPolicy<D,
DefaultMutContextPolicy>) {
-//! structural_map(1_i64, (mapper, |s: String| s),
WalkOrder::PreOrder).unwrap();
-//! }
-//! ```
-//!
-//! Nested callback tuples obey the same restriction.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: &mut MapWithContextPolicy<D,
DefaultMutContextPolicy>) {
-//! structural_map(1_i64, (|s: String| s, ((mapper,),)),
WalkOrder::PostOrder).unwrap();
-//! }
-//! ```
-//!
-//! Compose policies in a tuple instead of nesting mapper wrappers.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: MapWithContextPolicy<D,
DefaultMutContextPolicy>) {
-//! MapWithContextPolicy::new(mapper, DefaultMutContextPolicy);
-//! }
-//! ```
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index eedce47c..1ac54421 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -200,15 +200,8 @@ impl IntoVisitResult for Result<Option<VisitInterrupt>> {
}
}
-/// Fallible result returned by generated typed dispatch.
-#[doc(hidden)]
-pub type WalkCallbackResult = Result<WalkResult>;
-
pub use super::StructuralView;
-/// Compatibility name for [`StructuralView`].
-pub use super::StructuralView as VisitValue;
-
enum NativeHalt {
Interrupt(Any),
Error(Error),
@@ -623,14 +616,16 @@ pub use super::dispatch::{ByWalkDispatch, DispatchWalker,
WalkDispatch};
/// Conversion into the walker argument of [`structural_walk`].
///
-/// Accepts a mutable [`WalkDispatch`], a typed callback, or a nested callback
-/// tuple. `Marker` distinguishes the supported callback shapes.
+/// Accepts a mutable [`WalkDispatch`], a typed callback, a nested callback
+/// tuple, or an owned or borrowed [`WalkWithContextPolicy`].
+/// `Marker` distinguishes the supported callback shapes.
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a supported `structural_walk` walker",
note = "accepted walkers: `&mut V` where `V: WalkDispatch`; a closure over
`&StructuralView`, \
an FFI value type `T`, or `&N` of an object node type (`N:
ObjectCore`, e.g. \
`&Object`), optionally with a trailing `DefRegionKind` argument;
or a tuple of \
- up to 12 such links (tuples nest, so `(a, (b, c))` chains more)",
+ up to 12 such links (tuples nest, so `(a, (b, c))` chains more); \
+ or a `WalkWithContextPolicy` passed by value or mutable reference",
note = "closure arguments need explicit type annotations; ObjectRef
wrappers like `String` \
or `Array<T>` are FFI value types — take them by value, not by
reference"
)]
@@ -641,71 +636,25 @@ pub trait IntoWalker<Marker> {
fn into_walker(self) -> Self::Walker;
}
-/// Adapter for a catch-all walk callback.
-#[doc(hidden)]
-pub struct ClosureWalker<F> {
- callback: F,
-}
-
-impl<F, O> NativeVisit for ClosureWalker<F>
-where
- F: FnMut(&StructuralView) -> O,
- O: IntoWalkResult,
-{
- fn visit(
- &mut self,
- value: &StructuralView,
- _def_region_kind: DefRegionKind,
- ) -> Result<WalkResult> {
- (self.callback)(value).into_walk_result()
- }
-}
-
-#[doc(hidden)]
-pub enum ByValueClosure {}
-
-impl<F, O> IntoWalker<ByValueClosure> for F
+impl<F, O> IntoWalker<ByCatchAllLink> for F
where
- F: FnMut(&StructuralView) -> O,
+ F: for<'a> FnMut(&'a StructuralView) -> O,
O: IntoWalkResult,
{
- type Walker = ClosureWalker<F>;
+ type Walker = ChainWalker<F, ByCatchAllLink>;
fn into_walker(self) -> Self::Walker {
- ClosureWalker { callback: self }
+ ChainWalker::new(self)
}
}
-/// Catch-all walk adapter that also supplies the definition-region state.
-#[doc(hidden)]
-pub struct ClosureKindWalker<F> {
- callback: F,
-}
-
-impl<F, O> NativeVisit for ClosureKindWalker<F>
+impl<F, O> IntoWalker<ByCatchAllKindLink> for F
where
- F: FnMut(&StructuralView, DefRegionKind) -> O,
- O: IntoWalkResult,
-{
- fn visit(
- &mut self,
- value: &StructuralView,
- def_region_kind: DefRegionKind,
- ) -> Result<WalkResult> {
- (self.callback)(value, def_region_kind).into_walk_result()
- }
-}
-
-#[doc(hidden)]
-pub enum ByValueKindClosure {}
-
-impl<F, O> IntoWalker<ByValueKindClosure> for F
-where
- F: FnMut(&StructuralView, DefRegionKind) -> O,
+ F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
O: IntoWalkResult,
{
- type Walker = ClosureKindWalker<F>;
+ type Walker = ChainWalker<F, ByCatchAllKindLink>;
fn into_walker(self) -> Self::Walker {
- ClosureKindWalker { callback: self }
+ ChainWalker::new(self)
}
}
@@ -722,7 +671,7 @@ pub trait WalkChainLink<Marker>: sealed::SealedLink<Marker>
{
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult>;
+ ) -> Option<Result<WalkResult>>;
}
mod sealed {
@@ -783,7 +732,7 @@ where
&mut self,
value: &StructuralView,
_def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
value
.cast::<T>()
.map(|typed| self(typed).into_walk_result())
@@ -804,7 +753,7 @@ where
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
value
.cast::<T>()
.map(|typed| self(typed, def_region_kind).into_walk_result())
@@ -825,7 +774,7 @@ where
&mut self,
value: &StructuralView,
_def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
value
.as_node::<N>()
.map(|node| self(node).into_walk_result())
@@ -846,7 +795,7 @@ where
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
value
.as_node::<N>()
.map(|node| self(node, def_region_kind).into_walk_result())
@@ -866,7 +815,7 @@ where
&mut self,
value: &StructuralView,
_def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
Some(self(value).into_walk_result())
}
}
@@ -884,7 +833,7 @@ where
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
Some(self(value, def_region_kind).into_walk_result())
}
}
@@ -901,7 +850,7 @@ impl<V: WalkDispatch> WalkChainLink<ByWalkDispatchLink> for
&mut V {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
self.dispatch_walk(value, def_region_kind)
}
}
@@ -956,7 +905,7 @@ macro_rules! impl_chain_link {
&mut self,
value: &StructuralView,
def_region_kind: DefRegionKind,
- ) -> Option<WalkCallbackResult> {
+ ) -> Option<Result<WalkResult>> {
$(
if let Some(result) = self.$idx.try_call(value,
def_region_kind) {
return Some(result);
@@ -1176,6 +1125,18 @@ where
pub trait NativeVisit: Sized {
const CUSTOM_DESCENT: bool = false;
+ fn walk_root(&mut self, root: AnyView<'_>, order: WalkOrder) ->
Result<Option<VisitInterrupt>> {
+ let root = raw_of(root);
+ finish(match order {
+ WalkOrder::PreOrder => {
+ run_structural_visitor(root, self, walk_runtime_vtable::<Self,
true>())
+ }
+ WalkOrder::PostOrder => {
+ run_structural_visitor(root, self, walk_runtime_vtable::<Self,
false>())
+ }
+ })
+ }
+
fn visit(
&mut self,
value: &StructuralView,
@@ -1191,6 +1152,32 @@ pub trait NativeVisit: Sized {
}
}
+impl<V: NativeVisit> NativeVisit for &mut V {
+ const CUSTOM_DESCENT: bool = V::CUSTOM_DESCENT;
+
+ fn walk_root(&mut self, root: AnyView<'_>, order: WalkOrder) ->
Result<Option<VisitInterrupt>> {
+ // Register the actual visitor as the active context, not this
reference's
+ // stack slot: policy continuations validate that identity when
reentering.
+ (**self).walk_root(root, order)
+ }
+
+ fn visit(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<WalkResult> {
+ (**self).visit(value, def_region_kind)
+ }
+
+ fn default_visit_children<const PRE_ORDER: bool>(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<Option<VisitInterrupt>> {
+ (**self).default_visit_children::<PRE_ORDER>(value, def_region_kind)
+ }
+}
+
fn default_walk_children<V: NativeVisit, const PRE_ORDER: bool>(
visitor: &mut V,
value: &StructuralView,
@@ -1957,7 +1944,8 @@ where
/// in any [`WalkChainLink`] shape (catch-all `&StructuralView`, typed, or
node,
/// with an optional trailing [`DefRegionKind`]), or a tuple of such
/// callbacks tried in order — the C++ callback overloads and variadic
-/// chain. The walker owns recursion: the handler runs once per value,
+/// chain. A [`WalkWithContextPolicy`] may be passed by value or mutable
reference.
+/// The walker owns recursion: the handler runs once per value,
/// before or after the value's children according to `order`, and steers
/// traversal through the returned [`WalkResult`].
pub fn structural_walk<R, M, H>(
@@ -1969,21 +1957,7 @@ where
H: IntoWalker<M>,
for<'x> AnyView<'x>: From<&'x R>,
{
- let mut dispatch = walker.into_walker();
- let root = raw_of(AnyView::from(root));
- let result = match order {
- WalkOrder::PreOrder => run_structural_visitor(
- root,
- &mut dispatch,
- walk_runtime_vtable::<H::Walker, true>(),
- ),
- WalkOrder::PostOrder => run_structural_visitor(
- root,
- &mut dispatch,
- walk_runtime_vtable::<H::Walker, false>(),
- ),
- };
- finish(result)
+ walker.into_walker().walk_root(AnyView::from(root), order)
}
fn finish(result: NativeResult) -> Result<Option<VisitInterrupt>> {
diff --git a/rust/tvm-ffi/src/extra/structural_visit/policy.rs
b/rust/tvm-ffi/src/extra/structural_visit/policy.rs
index fed91afc..8b8e3d4c 100644
--- a/rust/tvm-ffi/src/extra/structural_visit/policy.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit/policy.rs
@@ -154,7 +154,7 @@ impl<State, V: StructuralVisitor +
VisitCallbackState<State>> VisitContextDriver
///
/// The dispatcher is also the state visible through the policy's context. Use
/// `#[dispatch(walk)]` or implement [`WalkDispatch`] to define its callbacks.
-/// Run repeatedly with [`Self::walk`], or pass this value to
[`structural_walk`].
+/// Pass this value or a mutable reference to [`structural_walk`].
pub struct WalkWithContextPolicy<Walker, Policy> {
walker: Walker,
policy: Rc<Policy>,
@@ -183,22 +183,6 @@ impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>>
WalkWithContextPolicy<
pub fn into_state(self) -> Walker {
self.walker
}
-
- /// Walk a root using this dispatcher and policy.
- pub fn walk<R>(&mut self, root: &R, order: WalkOrder) ->
Result<Option<VisitInterrupt>>
- where
- for<'x> AnyView<'x>: From<&'x R>,
- {
- let raw = raw_of(AnyView::from(root));
- finish(match order {
- WalkOrder::PreOrder => {
- run_structural_visitor(raw, self, walk_runtime_vtable::<Self,
true>())
- }
- WalkOrder::PostOrder => {
- run_structural_visitor(raw, self, walk_runtime_vtable::<Self,
false>())
- }
- })
- }
}
#[doc(hidden)]
@@ -213,6 +197,15 @@ impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>>
IntoWalker<ByPolicyWal
}
}
+impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>>
IntoWalker<ByPolicyWalk>
+ for &mut WalkWithContextPolicy<Walker, Policy>
+{
+ type Walker = Self;
+ fn into_walker(self) -> Self {
+ self
+ }
+}
+
impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>> NativeVisit
for WalkWithContextPolicy<Walker, Policy>
{
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index eadc138c..2bc2bb81 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -49,15 +49,15 @@ pub use crate::error::{
};
pub use crate::extra::module::Module;
pub use crate::extra::structural_mutate::{
- structural_map, structural_mutate, CallbackMutator,
DefaultMutContextPolicy, InplaceMode,
- InplaceValue, IntoMapResult, IntoMapper, IntoMutator, MapChainLink,
MapDispatch, MapValue,
- MapWithContextPolicy, MutContextPolicy, MutateCallbacks, MutateChainLink,
MutateContext,
- MutateDispatch, MutateValue, Mutator, StructuralMutator,
StructuralVarRemap,
+ structural_map, structural_mutate, DefaultMutContextPolicy, InplaceMode,
InplaceValue,
+ IntoMapper, IntoMutator, MapChainLink, MapDispatch, MapWithContextPolicy,
MutContextPolicy,
+ MutateCallbacks, MutateChainLink, MutateContext, MutateDispatch,
MutateValue, Mutator,
+ StructuralMutator, StructuralVarRemap,
};
pub use crate::extra::structural_visit::{
structural_visit, structural_walk, ContextPolicy, DefRegionKind,
DefaultContextPolicy,
IntoVisitor, IntoWalkResult, IntoWalker, StructuralVisitor,
VisitCallbacks, VisitChainLink,
- VisitContext, VisitInterrupt, VisitValue, WalkChainLink, WalkDispatch,
WalkOrder, WalkResult,
+ VisitContext, VisitInterrupt, WalkChainLink, WalkDispatch, WalkOrder,
WalkResult,
WalkWithContextPolicy,
};
pub use crate::extra::unchanged::{Unchanged, UnchangedOr};
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index 70183e9c..fe1d0f81 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -22,11 +22,11 @@ use tvm_ffi::collections::map::MapObj;
use tvm_ffi::function::FunctionObj;
use tvm_ffi::object::ObjectRef;
use tvm_ffi::{
- dispatch, structural_map, structural_mutate, Any, AnyView, Array,
CallbackMutator,
- DefRegionKind, DefaultMutContextPolicy, Error, FieldGetter, Function,
InplaceMode,
- InplaceValue, IntoMapper, Map, MapDispatch, MapValue,
MapWithContextPolicy, MutContextPolicy,
- MutateCallbacks, MutateValue, Mutator, Object, ObjectArc, ObjectRefCore,
Result,
- String as FfiString, StructuralMutator, StructuralVarRemap, TypeIndex,
Unchanged, UnchangedOr,
+ dispatch, structural_map, structural_mutate, Any, AnyView, Array,
DefRegionKind,
+ DefaultMutContextPolicy, Error, FieldGetter, Function, InplaceMode,
InplaceValue, IntoMapper,
+ Map, MapDispatch, MapWithContextPolicy, MutContextPolicy, MutateCallbacks,
MutateContext,
+ MutateValue, Mutator, Object, ObjectArc, ObjectRefCore, Result, String as
FfiString,
+ StructuralMutator, StructuralVarRemap, StructuralView, TypeIndex,
Unchanged, UnchangedOr,
WalkOrder, RUNTIME_ERROR,
};
@@ -35,7 +35,7 @@ struct IncrementIntegers;
impl MapDispatch for IncrementIntegers {
fn dispatch_map(
&mut self,
- value: &MapValue,
+ value: &StructuralView,
_def_region_kind: DefRegionKind,
) -> Option<Result<Any>> {
value
@@ -50,7 +50,11 @@ struct ManualIncrement {
}
impl StructuralMutator for ManualIncrement {
- fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind:
DefRegionKind) -> Result<Any> {
+ fn dispatch_mutate(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<Any> {
if let Some(integer) = value.cast::<i64>() {
Ok(Any::from(integer + 1))
} else {
@@ -66,11 +70,11 @@ impl StructuralMutator for ManualIncrement {
self.default_maybe_inplace_mutate(value, def_region_kind)
}
- fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+ fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
self.remap.get(var)
}
- fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) ->
Result<()> {
+ fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()> {
self.remap.set(var, mutated_value)
}
}
@@ -82,7 +86,11 @@ struct ReplaceNone {
}
impl StructuralMutator for ReplaceNone {
- fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind:
DefRegionKind) -> Result<Any> {
+ fn dispatch_mutate(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<Any> {
if value.type_index() == TypeIndex::kTVMFFINone as i32 {
self.calls += 1;
Ok(Any::from(8i64))
@@ -99,11 +107,11 @@ impl StructuralMutator for ReplaceNone {
self.default_maybe_inplace_mutate(value, def_region_kind)
}
- fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+ fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
self.remap.get(var)
}
- fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) ->
Result<()> {
+ fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()> {
self.remap.set(var, mutated_value)
}
}
@@ -115,7 +123,11 @@ struct RecursiveEntryMutator {
}
impl StructuralMutator for RecursiveEntryMutator {
- fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind:
DefRegionKind) -> Result<Any> {
+ fn dispatch_mutate(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<Any> {
if value.type_index() == TypeIndex::kTVMFFINone as i32 {
if self.use_owned_value {
let value = Array::new(vec![1i64]);
@@ -139,11 +151,11 @@ impl StructuralMutator for RecursiveEntryMutator {
self.default_maybe_inplace_mutate(value, def_region_kind)
}
- fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+ fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
self.remap.get(var)
}
- fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) ->
Result<()> {
+ fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()> {
self.remap.set(var, mutated_value)
}
}
@@ -283,7 +295,7 @@ fn
none_values_are_dispatched_to_map_callbacks_and_user_mutators() {
let mut map_calls = 0;
let mapped = structural_map(
Any::new(),
- |value: &MapValue| {
+ |value: &StructuralView| {
map_calls += 1;
assert_eq!(value.type_index(), TypeIndex::kTVMFFINone as i32);
Any::from(7i64)
@@ -337,7 +349,7 @@ fn
default_mutation_mode_preserves_ownership_and_unchanged_results() {
increment: bool,
}
impl StructuralMutator for Controlled {
- fn dispatch_mutate(&mut self, value: &MapValue, _: DefRegionKind) ->
Result<Any> {
+ fn dispatch_mutate(&mut self, value: &StructuralView, _:
DefRegionKind) -> Result<Any> {
let integer = value.cast::<i64>().unwrap();
Ok(if self.increment {
Any::from(integer + 1)
@@ -410,12 +422,12 @@ fn
owned_entry_mode_is_forwarded_by_generated_and_closure_callbacks() {
let result = structural_mutate(
true,
(
- |_: bool, ctx: &mut CallbackMutator| {
+ |_: bool, ctx: &mut MutateContext<'_>| {
let child = Array::new(vec![1_i64]);
pointer.set(array_pointer(&child) as usize);
ctx.maybe_inplace_mutate_with_mode(child,
DefRegionKind::Pattern, mode)
},
- |value: i64, ctx: &mut CallbackMutator| {
+ |value: i64, ctx: &mut MutateContext<'_>| {
assert_eq!(ctx.def_region_kind(), DefRegionKind::Pattern);
value + 1
},
@@ -463,16 +475,13 @@ fn
consuming_callbacks_forward_permissions_without_temporary_owners() {
&mut self,
value: MutateValue<'_, Array<Any>>,
ctx: &mut Mutator,
- mode: InplaceMode,
) -> Result<UnchangedOr<Any>> {
- assert_eq!(mode, ctx.inplace_mode());
- assert_eq!(mode, value.inplace_mode());
+ assert_eq!(ctx.inplace_mode(), value.inplace_mode());
self.observe(&value);
ctx.default_mutate_with_mode_result(self, value, self.requested)
}
- fn mutate_integer(&mut self, value: i64, ctx: &mut Mutator, mode:
InplaceMode) -> i64 {
- assert_eq!(mode, InplaceMode::Disallow);
- assert_eq!(mode, ctx.inplace_mode());
+ fn mutate_integer(&mut self, value: i64, ctx: &mut Mutator) -> i64 {
+ assert_eq!(ctx.inplace_mode(), InplaceMode::Disallow);
value + 1
}
}
@@ -499,11 +508,11 @@ fn
consuming_callbacks_forward_permissions_without_temporary_owners() {
structural_mutate(root, &mut state).unwrap()
} else {
let miss = |_: MutateValue<'_, FfiString>,
- _: &mut CallbackMutator<Forward>|
+ _: &mut MutateContext<'_, Forward>|
-> Any { panic!("typed miss must continue") };
- let increment = |value: i64, _: &mut CallbackMutator<Forward>|
value + 1;
+ let increment = |value: i64, _: &mut MutateContext<'_,
Forward>| value + 1;
let descend = |value: MutateValue<'_, Array<Any>>,
- ctx: &mut CallbackMutator<Forward>| {
+ ctx: &mut MutateContext<'_, Forward>| {
assert_eq!(value.inplace_mode(), ctx.inplace_mode());
ctx.state_mut().observe(&value);
let requested = ctx.state().requested;
@@ -579,7 +588,7 @@ fn
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
let mut callbacks = MutateCallbacks::new(
inner,
(
- |_: bool, ctx: &mut CallbackMutator<Inner<'_>>| ->
Result<Any> {
+ |_: bool, ctx: &mut MutateContext<'_, Inner<'_>>| ->
Result<Any> {
let value = ctx.state_mut().value.take().unwrap();
let result = if ctx.state().preserve_unchanged {
ctx.default_maybe_inplace_mutate_result(value)?.into()
@@ -589,7 +598,7 @@ fn
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
ctx.state_mut().result = result;
Ok(Any::new())
},
- |value: i64, _: &mut CallbackMutator<Inner<'_>>| value + 1,
+ |value: i64, _: &mut MutateContext<'_, Inner<'_>>| value +
1,
),
);
structural_mutate(true, &mut callbacks)?;
@@ -618,7 +627,7 @@ fn
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
let result = if generated {
structural_mutate(root, &mut Outer { preserve, retain })
} else {
- structural_mutate(root, |value: MutateValue<'_>, _: &mut
CallbackMutator| {
+ structural_mutate(root, |value: MutateValue<'_>, _: &mut
MutateContext<'_>| {
transfer(value, false, preserve, retain)
})
}
@@ -638,7 +647,7 @@ fn
consuming_default_descent_preserves_unchanged_and_propagates_errors() {
let pointer = array_pointer(&root);
let result = structural_mutate(
root,
- |value: MutateValue<'_>, ctx: &mut CallbackMutator| ->
Result<UnchangedOr<Any>> {
+ |value: MutateValue<'_>, ctx: &mut MutateContext<'_>| ->
Result<UnchangedOr<Any>> {
if value.cast::<i64>().is_some() {
return if fail {
Err(Error::new(RUNTIME_ERROR, "child failed", ""))
@@ -751,7 +760,7 @@ fn
callback_errors_preserve_message_and_add_object_context() {
let error = match structural_mutate(
Array::new(vec![1i64]),
- |_integer: i64, _mutator: &mut CallbackMutator| -> Result<i64> {
+ |_integer: i64, _mutator: &mut MutateContext<'_>| -> Result<i64> {
Err(Error::new(
RUNTIME_ERROR,
"callback mutator failed",
@@ -943,7 +952,7 @@ struct GeneratedLeafDispatch {
#[dispatch(mutate)]
impl GeneratedLeafDispatch {
fn mutate_integer(&mut self, value: i64, mutator: &mut Mutator) -> Any {
- let region = mutator.region();
+ let region = mutator.def_region_kind();
self.integers.push((value, region));
Any::from(value + 1)
}
@@ -994,7 +1003,7 @@ struct GeneratedRecursiveDispatch {
#[dispatch(mutate)]
impl GeneratedRecursiveDispatch {
fn mutate_array(&mut self, array: Array<i64>, mutator: &mut Mutator) ->
Result<Array<i64>> {
- let region = mutator.region();
+ let region = mutator.def_region_kind();
self.arrays.push(region);
let mut mutated = Vec::with_capacity(array.len());
for value in array.iter() {
@@ -1004,7 +1013,7 @@ impl GeneratedRecursiveDispatch {
}
fn mutate_integer(&mut self, value: i64, mutator: &mut Mutator) -> Any {
- let region = mutator.region();
+ let region = mutator.def_region_kind();
self.integers.push((value, region));
Any::from(value + 10)
}
@@ -1073,7 +1082,7 @@ fn pre_order_retained_alias_disables_in_place_mutation() {
let mut retained = None;
let mapped = structural_map(
root,
- |value: &MapValue| {
+ |value: &StructuralView| {
if value.type_index() == TypeIndex::kTVMFFIList as i32 {
retained = Some(value.to_owned());
value.to_owned()
@@ -1151,7 +1160,7 @@ fn callbacks_return_values_convertible_into_any() {
let mutated = structural_mutate(
Array::new(vec![1i64, 2]),
- |integer: i64, _mutator: &mut CallbackMutator| integer * 2,
+ |integer: i64, _mutator: &mut MutateContext<'_>| integer * 2,
)
.and_then(Array::<i64>::try_from)
.unwrap();
@@ -1161,8 +1170,8 @@ fn callbacks_return_values_convertible_into_any() {
#[test]
fn recursive_mutate_returns_unchanged_or_a_replacement() {
fn clamp_negative_integers(
- value: &MapValue,
- mutator: &mut CallbackMutator,
+ value: &StructuralView,
+ mutator: &mut MutateContext<'_>,
) -> Result<UnchangedOr<Any>> {
if let Some(integer) = value.cast::<i64>() {
if integer >= 0 {
@@ -1211,7 +1220,7 @@ fn pre_order_unchanged_reuses_unmodified_subtrees() {
}
},
// Keeping an array still lets pre-order map transform its
children.
- |_value: &MapValue| Unchanged,
+ |_value: &StructuralView| Unchanged,
),
WalkOrder::PreOrder,
)
@@ -1233,6 +1242,42 @@ fn pre_order_unchanged_reuses_unmodified_subtrees() {
);
}
+#[test]
+fn post_order_unchanged_preserves_descendant_rewrites() {
+ for with_policy in [false, true] {
+ for shared in [false, true] {
+ let root = Array::new(vec![1_i64]);
+ let pointer = array_pointer(&root);
+ let alias = shared.then(|| root.clone());
+ let callbacks = (
+ |integer: i64| integer + 1,
+ |value: &StructuralView| {
+
assert_eq!(value.cast::<Array<i64>>().unwrap().get(0).unwrap(), 2);
+ Unchanged
+ },
+ );
+ let mapped = if with_policy {
+ structural_map(
+ root,
+ MapWithContextPolicy::new(callbacks.into_mapper(),
DefaultMutContextPolicy),
+ WalkOrder::PostOrder,
+ )
+ } else {
+ structural_map(root, callbacks, WalkOrder::PostOrder)
+ }
+ .and_then(Array::<i64>::try_from)
+ .unwrap();
+ assert_eq!(mapped.get(0).unwrap(), 2);
+ if let Some(alias) = alias {
+ assert_eq!(alias.get(0).unwrap(), 1);
+ assert_ne!(array_pointer(&mapped), pointer);
+ } else {
+ assert_eq!(array_pointer(&mapped), pointer);
+ }
+ }
+ }
+}
+
#[test]
fn twelve_link_tuple_reaches_final_map_dispatch() {
let mut final_dispatch = IncrementIntegers;
@@ -1285,7 +1330,7 @@ fn nested_tuple_chain_exceeds_flat_arity() {
|value: i64| Any::from(value * 10),
(
|value: Array<FfiString>| Any::from(value),
- (|value: &MapValue| {
+ (|value: &StructuralView| {
catch_all += 1;
value.to_owned()
},),
@@ -1306,7 +1351,7 @@ fn callbacks_run_in_the_configured_order() {
let mut pre = Vec::new();
structural_map(
root.clone(),
- |value: &MapValue| {
+ |value: &StructuralView| {
pre.push(value.cast::<i64>());
value.to_owned()
},
@@ -1318,7 +1363,7 @@ fn callbacks_run_in_the_configured_order() {
let mut post = Vec::new();
structural_map(
root,
- |value: &MapValue| {
+ |value: &StructuralView| {
post.push(value.cast::<i64>());
value.to_owned()
},
@@ -1381,7 +1426,7 @@ fn map_keys_are_anchors_and_object_leaves_are_preserved()
{
fn callback_mutate_defaults_unmatched_values_and_preserves_root_permit() {
let root = Array::new(vec![1i64, 2]);
let root_pointer = array_pointer(&root);
- let mutated = structural_mutate(root, |value: i64, _mutator: &mut
CallbackMutator| {
+ let mutated = structural_mutate(root, |value: i64, _mutator: &mut
MutateContext<'_>| {
Any::from(value + 1)
})
.and_then(Array::<i64>::try_from)
@@ -1396,14 +1441,17 @@ struct CallbackMutateStats {
defaults: usize,
}
-fn stateful_mutate_integer(value: i64, mutator: &mut
CallbackMutator<CallbackMutateStats>) -> Any {
+fn stateful_mutate_integer(
+ value: i64,
+ mutator: &mut MutateContext<'_, CallbackMutateStats>,
+) -> Any {
mutator.state_mut().integers.push(value);
Any::from(value + 1)
}
fn stateful_mutate_default(
value: &tvm_ffi::StructuralView,
- mutator: &mut CallbackMutator<CallbackMutateStats>,
+ mutator: &mut MutateContext<'_, CallbackMutateStats>,
) -> Result<Any> {
mutator.state_mut().defaults += 1;
mutator.default_mutate(value)
@@ -1441,8 +1489,8 @@ struct CallbackMutateDepth {
}
fn stateful_mutate_recursive(
- value: &MapValue,
- mutator: &mut CallbackMutator<CallbackMutateDepth>,
+ value: &StructuralView,
+ mutator: &mut MutateContext<'_, CallbackMutateDepth>,
) -> Result<Any> {
{
let state = mutator.state_mut();
@@ -1484,8 +1532,8 @@ fn
callback_mutate_explicit_default_is_repeatable_copy_path() {
let mutated = structural_mutate(
root,
(
- |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
- |value: &MapValue, mutator: &mut CallbackMutator| -> Result<Any> {
+ |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value +
1),
+ |value: &StructuralView, mutator: &mut MutateContext<'_>| ->
Result<Any> {
defaults.set(defaults.get() + 1);
let first = mutator.default_mutate(value)?;
let second = mutator.default_mutate(value)?;
@@ -1507,8 +1555,10 @@ fn
callback_mutate_match_is_final_and_same_fn_can_reenter() {
let mutated = structural_mutate(
Array::new(vec![1i64]),
(
- |_array: Array<i64>, _mutator: &mut CallbackMutator|
Any::from(Array::new(vec![10i64])),
- |value: i64, _mutator: &mut CallbackMutator| {
+ |_array: Array<i64>, _mutator: &mut MutateContext<'_>| {
+ Any::from(Array::new(vec![10i64]))
+ },
+ |value: i64, _mutator: &mut MutateContext<'_>| {
integer_calls.set(integer_calls.get() + 1);
Any::from(value + 1)
},
@@ -1522,7 +1572,7 @@ fn
callback_mutate_match_is_final_and_same_fn_can_reenter() {
let calls = Cell::new(0);
let mutated = structural_mutate(
Array::new(vec![1i64, 2]),
- |value: &MapValue, mutator: &mut CallbackMutator| {
+ |value: &StructuralView, mutator: &mut MutateContext<'_>| {
calls.set(calls.get() + 1);
mutator.default_mutate(value)
},
@@ -1547,10 +1597,10 @@ fn
callback_mutate_supports_node_links_nested_tuples_and_reflection() {
root,
(
(
- |_value: bool, _mutator: &mut CallbackMutator| Any::new(),
- |_node: &FunctionObj, _mutator: &mut CallbackMutator|
Any::from(7i64),
+ |_value: bool, _mutator: &mut MutateContext<'_>| Any::new(),
+ |_node: &FunctionObj, _mutator: &mut MutateContext<'_>|
Any::from(7i64),
),
- |value: i64, mutator: &mut CallbackMutator| {
+ |value: i64, mutator: &mut MutateContext<'_>| {
regions.borrow_mut().push(mutator.def_region_kind());
Any::from(value + 1)
},
@@ -1576,8 +1626,8 @@ fn
callback_mutate_distinguishes_borrowed_and_owned_children() {
let mutated = structural_mutate(
true,
(
- |_value: bool, mutator: &mut CallbackMutator|
mutator.mutate(&borrowed_child),
- |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
+ |_value: bool, mutator: &mut MutateContext<'_>|
mutator.mutate(&borrowed_child),
+ |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value +
1),
),
)
.and_then(Array::<i64>::try_from)
@@ -1590,12 +1640,12 @@ fn
callback_mutate_distinguishes_borrowed_and_owned_children() {
let mutated = structural_mutate(
true,
(
- |_value: bool, mutator: &mut CallbackMutator| {
+ |_value: bool, mutator: &mut MutateContext<'_>| {
let child = Array::new(vec![1i64]);
owned_pointer.set(array_pointer(&child) as usize);
mutator.maybe_inplace_mutate(child)
},
- |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
+ |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value +
1),
),
)
.and_then(Array::<i64>::try_from)
@@ -1608,11 +1658,11 @@ fn
callback_mutate_distinguishes_borrowed_and_owned_children() {
fn nested_callback_mutate_restores_the_outer_active_mutator() {
let mutated = structural_mutate(
1i64,
- |value: i64, mutator: &mut CallbackMutator| -> Result<Any> {
+ |value: i64, mutator: &mut MutateContext<'_>| -> Result<Any> {
if value != 1 {
return Ok(Any::from(value + 1));
}
- let inner = structural_mutate(2i64, |value: i64, _mutator: &mut
CallbackMutator| {
+ let inner = structural_mutate(2i64, |value: i64, _mutator: &mut
MutateContext<'_>| {
Any::from(value + 10)
})?;
assert_eq!(i64::try_from(inner).unwrap(), 12);
@@ -1629,7 +1679,7 @@ fn
callback_mutate_panics_resume_and_leave_the_next_run_usable() {
let panic = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(||
{
structural_mutate(
Array::new(vec![1i64]),
- |_value: i64, _mutator: &mut CallbackMutator| -> Any {
+ |_value: i64, _mutator: &mut MutateContext<'_>| -> Any {
panic!("callback mutator panic")
},
)
@@ -1644,7 +1694,7 @@ fn
callback_mutate_panics_resume_and_leave_the_next_run_usable() {
let mutated = structural_mutate(
Array::new(vec![1i64]),
- |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
+ |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value + 1),
)
.and_then(Array::<i64>::try_from)
.unwrap();
@@ -1673,7 +1723,7 @@ impl MutContextPolicy<PolicyState> for ArrayPolicy {
fn default_mutate(
&self,
value: MutateValue<'_>,
- ctx: &mut CallbackMutator<PolicyState>,
+ ctx: &mut MutateContext<'_, PolicyState>,
) -> Result<UnchangedOr<Any>> {
if value
.as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1696,7 +1746,7 @@ impl MutContextPolicy<PolicyState> for RecordPolicy {
fn default_mutate(
&self,
value: MutateValue<'_>,
- ctx: &mut CallbackMutator<PolicyState>,
+ ctx: &mut MutateContext<'_, PolicyState>,
) -> Result<UnchangedOr<Any>> {
if value
.as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1760,12 +1810,12 @@ fn
mutation_policies_share_state_and_preserve_callback_order() {
let mut mutator = MutateCallbacks::new(
PolicyState::default(),
(
- |x: i64, ctx: &mut CallbackMutator<PolicyState>| {
+ |x: i64, ctx: &mut MutateContext<'_, PolicyState>| {
let depth = ctx.state().depth;
ctx.state_mut().events.push(("integer", depth));
x + 1
},
- |value: MutateValue<'_>, ctx: &mut CallbackMutator<PolicyState>| {
+ |value: MutateValue<'_>, ctx: &mut MutateContext<'_, PolicyState>|
{
let depth = ctx.state().depth;
ctx.state_mut().events.push(("callback", depth));
ctx.default_maybe_inplace_mutate_result(value)
@@ -1786,7 +1836,7 @@ fn
map_policy_entries_preserve_descent_and_callback_composition() {
fn default_mutate(
&self,
_: MutateValue<'_>,
- _: &mut CallbackMutator<State>,
+ _: &mut MutateContext<'_, State>,
) -> Result<UnchangedOr<Any>> {
Ok(UnchangedOr::unchanged())
}
@@ -1795,15 +1845,14 @@ fn
map_policy_entries_preserve_descent_and_callback_composition() {
let root = || Array::new(vec![1_i64]);
let first = |value: Any|
Array::<i64>::try_from(value).unwrap().get(0).unwrap();
for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
- for entry in 0..3 {
+ for entry in 0..2 {
let mut state = PolicyState::default();
let output = {
let mut mapper =
MapWithContextPolicy::new(&mut state,
(DefaultMutContextPolicy, Stop));
match entry {
0 => structural_map(root(), mapper, order),
- 1 => structural_map(root(), &mut mapper, order),
- _ => mapper.map(root(), order),
+ _ => structural_map(root(), &mut mapper, order),
}
}
.unwrap();
@@ -1846,7 +1895,7 @@ fn
mutation_policy_continuations_preserve_ownership_and_markers() {
fn default_mutate(
&self,
value: MutateValue<'_>,
- ctx: &mut CallbackMutator<Ownership>,
+ ctx: &mut MutateContext<'_, Ownership>,
) -> Result<UnchangedOr<Any>> {
let array = value
.as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1886,25 +1935,25 @@ fn
mutation_policy_continuations_preserve_ownership_and_markers() {
};
let (output, state) = if entry < 2 {
let mut mapper = MapWithContextPolicy::new(state, policy);
- let output = mapper
- .map(
- root,
- if entry == 0 {
- WalkOrder::PreOrder
- } else {
- WalkOrder::PostOrder
- },
- )
- .unwrap();
+ let output = structural_map(
+ root,
+ &mut mapper,
+ if entry == 0 {
+ WalkOrder::PreOrder
+ } else {
+ WalkOrder::PostOrder
+ },
+ )
+ .unwrap();
(output, mapper.into_state())
} else {
let mut mutator = MutateCallbacks::new(
state,
(
- |x: i64, ctx: &mut CallbackMutator<Ownership>| {
+ |x: i64, ctx: &mut MutateContext<'_, Ownership>| {
ctx.state_mut().map_integer(x)
},
- |value: MutateValue<'_>, ctx: &mut
CallbackMutator<Ownership>| {
+ |value: MutateValue<'_>, ctx: &mut
MutateContext<'_, Ownership>| {
ctx.default_maybe_inplace_mutate_result(value)
},
),
@@ -1948,7 +1997,7 @@ fn
mutation_policy_regions_retargeting_and_error_restore() {
fn default_mutate(
&self,
value: MutateValue<'_>,
- ctx: &mut CallbackMutator<Regions>,
+ ctx: &mut MutateContext<'_, Regions>,
) -> Result<UnchangedOr<Any>> {
if value.cast::<bool>() == Some(false) {
// Bypass this container's callback, but enter the next policy
and redispatch its children.
@@ -1975,7 +2024,7 @@ fn
mutation_policy_regions_retargeting_and_error_restore() {
fn default_mutate(
&self,
value: MutateValue<'_>,
- ctx: &mut CallbackMutator<Regions>,
+ ctx: &mut MutateContext<'_, Regions>,
) -> Result<UnchangedOr<Any>> {
if value
.as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1996,7 +2045,7 @@ fn
mutation_policy_regions_retargeting_and_error_restore() {
for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
let mut mapper =
MapWithContextPolicy::new(Regions::default(),
(Redirect(requested), Observe));
- let output = mapper.map(false, order).unwrap();
+ let output = structural_map(false, &mut mapper, order).unwrap();
assert_eq!(i64::try_from(array_item(&output, 0)).unwrap(), 1);
let mut expected = vec![(100, Pattern), (1, Pattern)];
if order == WalkOrder::PostOrder {
@@ -2008,7 +2057,7 @@ fn
mutation_policy_regions_retargeting_and_error_restore() {
.unwrap()
.call_tuple((false,))
.unwrap();
- mapper.map(graph, order).unwrap();
+ structural_map(graph, &mut mapper, order).unwrap();
let expected = if order == WalkOrder::PreOrder {
vec![(1, Simple), (2, Pattern), (99, Pattern), (3, Use)]
} else {
@@ -2018,7 +2067,7 @@ fn
mutation_policy_regions_retargeting_and_error_restore() {
}
let mut mutator = MutateCallbacks::new(
Regions::default(),
- |value: MutateValue<'_>, ctx: &mut CallbackMutator<Regions>| {
+ |value: MutateValue<'_>, ctx: &mut MutateContext<'_, Regions>| {
if let Some(x) = value.cast::<i64>() {
let kind = ctx.def_region_kind();
ctx.state_mut().0.push((x, kind));
@@ -2057,7 +2106,7 @@ fn
mutation_policy_halts_restore_state_and_skip_later_policies() {
fn default_mutate(
&self,
_: MutateValue<'_>,
- _: &mut CallbackMutator<PolicyState>,
+ _: &mut MutateContext<'_, PolicyState>,
) -> Result<UnchangedOr<Any>> {
if self.0 {
Err(Error::new(RUNTIME_ERROR, "stop descent", ""))
@@ -2072,7 +2121,7 @@ fn
mutation_policy_halts_restore_state_and_skip_later_policies() {
PolicyState::default(),
(ArrayPolicy, (Halt(fail), RecordPolicy)),
);
- let result = mapper.map(Array::new(vec![1_i64]), order);
+ let result = structural_map(Array::new(vec![1_i64]), &mut mapper,
order);
assert_eq!(result.is_err(), fail);
assert_eq!(mapper.state().depth, 0);
assert!(!mapper
@@ -2086,7 +2135,7 @@ fn
mutation_policy_halts_restore_state_and_skip_later_policies() {
}
let mut mutator = MutateCallbacks::new(
PolicyState::default(),
- |x: i64, _: &mut CallbackMutator<PolicyState>| x + 1,
+ |x: i64, _: &mut MutateContext<'_, PolicyState>| x + 1,
)
.with_policy((ArrayPolicy, (Halt(fail), RecordPolicy)));
assert_eq!(
diff --git a/rust/tvm-ffi/tests/test_structural_visit.rs
b/rust/tvm-ffi/tests/test_structural_visit.rs
index 22137fa6..4901b305 100644
--- a/rust/tvm-ffi/tests/test_structural_visit.rs
+++ b/rust/tvm-ffi/tests/test_structural_visit.rs
@@ -22,8 +22,8 @@ use tvm_ffi::object::ObjectRef;
use tvm_ffi::{
dispatch, get_type_attr, structural_visit, structural_walk, Any, Array,
ContextPolicy,
DLDataType, DLDataTypeCode, DefRegionKind, Error, FieldGetter, Function,
Map, Object,
- ObjectRefCore, Result, String as FfiString, StructuralVisitor, TypeIndex,
VisitCallbacks,
- VisitContext, VisitInterrupt, VisitValue, WalkOrder, WalkResult,
WalkWithContextPolicy,
+ ObjectRefCore, Result, String as FfiString, StructuralView,
StructuralVisitor, TypeIndex,
+ VisitCallbacks, VisitContext, VisitInterrupt, WalkOrder, WalkResult,
WalkWithContextPolicy,
RUNTIME_ERROR,
};
@@ -53,7 +53,7 @@ fn
composed_policies_share_array_scope_with_visit_and_walk_callbacks() {
impl ContextPolicy<CollectIntegers> for ArrayScope {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, CollectIntegers>,
) -> Result<Option<VisitInterrupt>> {
if value.cast::<Array<Any>>().is_none() {
@@ -78,7 +78,7 @@ fn
composed_policies_share_array_scope_with_visit_and_walk_callbacks() {
impl ContextPolicy<CollectIntegers> for RecordDescent {
fn default_visit(
&self,
- _value: &VisitValue,
+ _value: &StructuralView,
visitor: &mut VisitContext<'_, CollectIntegers>,
) -> Result<Option<VisitInterrupt>> {
let state = visitor.state_mut();
@@ -122,10 +122,18 @@ fn
composed_policies_share_array_scope_with_visit_and_walk_callbacks() {
for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
let mut walker =
WalkWithContextPolicy::new(CollectIntegers::default(),
(ArrayScope, RecordDescent));
- assert!(walker.walk(&root, order).unwrap().is_none());
- assert_eq!(walker.state().integers, expected);
- assert_eq!(walker.state().depth, 0);
- assert_eq!(walker.state().descent_depths, vec![1, 1, 2, 2, 2, 1]);
+ for run in 1..=2 {
+ assert!(structural_walk(&root, &mut walker, order)
+ .unwrap()
+ .is_none());
+ assert_eq!(walker.state().integers, expected.repeat(run));
+ assert_eq!(walker.state().depth, 0);
+ assert_eq!(
+ walker.state().descent_depths,
+ [1, 1, 2, 2, 2, 1].repeat(run)
+ );
+ }
+ assert!(structural_walk(&root, walker, order).unwrap().is_none());
}
}
@@ -170,7 +178,7 @@ fn
policy_continuation_scopes_regions_and_restores_after_halts() {
impl ContextPolicy<Probe> for Scope {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
ctx: &mut VisitContext<'_, Probe>,
) -> Result<Option<VisitInterrupt>> {
let Some(array) = value.cast::<Array<i64>>() else {
@@ -197,7 +205,12 @@ fn
policy_continuation_scopes_regions_and_restores_after_halts() {
let policies = (Scope(Simple, 4), (Scope(Pattern, 3), Scope(Use,
2)));
let (result, state) = if let Some(order) = order {
let mut walker = WalkWithContextPolicy::new(state, policies);
- let result = walker.walk(&root, order);
+ let result = structural_walk(&root, &mut walker, order);
+ // Reuse the same borrowed walker after completion,
interruption,
+ // or error. The new root must begin outside every prior
region.
+ assert!(structural_walk(&99_i64, &mut walker, order)
+ .unwrap()
+ .is_none());
(result, walker.into_state())
} else {
let mut visitor =
@@ -227,6 +240,9 @@ fn
policy_continuation_scopes_regions_and_restores_after_halts() {
}
// Unwind the innermost, middle, and outer scopes.
expected.extend([(2, Pattern), (3, Simple), (4, Use)]);
+ if order.is_some() {
+ expected.push((99, Use));
+ }
assert_eq!(state.seen, expected);
}
}
@@ -280,7 +296,7 @@ fn
policy_continuation_retargets_without_dispatching_the_container() {
impl ContextPolicy<Probe> for Redirect {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
ctx: &mut VisitContext<'_, Probe>,
) -> Result<Option<VisitInterrupt>> {
let Some(array) = value.cast::<Array<Any>>() else {
@@ -307,7 +323,7 @@ fn
policy_continuation_retargets_without_dispatching_the_container() {
impl ContextPolicy<Probe> for Observe {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
ctx: &mut VisitContext<'_, Probe>,
) -> Result<Option<VisitInterrupt>> {
if let Some(array) = value.cast::<Array<Any>>() {
@@ -334,7 +350,9 @@ fn
policy_continuation_retargets_without_dispatching_the_container() {
for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
for skip in [false, true] {
let mut walker = WalkWithContextPolicy::new(Probe(vec![], skip),
(Redirect, Observe));
- assert!(walker.walk(&root, order).unwrap().is_none());
+ assert!(structural_walk(&root, &mut walker, order)
+ .unwrap()
+ .is_none());
let mut expected = descent.clone();
let array_callback = ("array callback", 2, DefRegionKind::None);
match order {
@@ -351,7 +369,7 @@ fn
policy_continuation_retargets_without_dispatching_the_container() {
}
let mut visitor = VisitCallbacks::new(
Probe::default(),
- |value: &VisitValue, ctx: &mut VisitContext<'_, Probe>| {
+ |value: &StructuralView, ctx: &mut VisitContext<'_, Probe>| {
let kind = ctx.def_region_kind();
if let Some(array) = value.cast::<Array<Any>>() {
ctx.state_mut()
@@ -377,7 +395,7 @@ fn
policy_continuation_retargets_without_dispatching_the_container() {
let target = visit_region_graph(false);
let mut visitor = VisitCallbacks::new(
Probe::default(),
- |value: &VisitValue, ctx: &mut VisitContext<'_, Probe>| {
+ |value: &StructuralView, ctx: &mut VisitContext<'_, Probe>| {
if value.cast::<i64>() == Some(-1) {
assert!(ctx
.default_visit_children(&Any::new(),
DefRegionKind::Pattern)?
@@ -416,7 +434,7 @@ fn
policy_halts_skip_remaining_policies_and_restore_outer_state() {
}
#[dispatch(walk)]
impl Probe {
- fn walk_any(&mut self, value: &VisitValue, kind: DefRegionKind) ->
WalkResult {
+ fn walk_any(&mut self, value: &StructuralView, kind: DefRegionKind) ->
WalkResult {
assert!(
value.cast::<Array<i64>>().is_some(),
"children must not be visited"
@@ -430,7 +448,7 @@ fn
policy_halts_skip_remaining_policies_and_restore_outer_state() {
impl ContextPolicy<Probe> for Scope {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
ctx: &mut VisitContext<'_, Probe>,
) -> Result<Option<VisitInterrupt>> {
ctx.state_mut().events.push("enter");
@@ -446,7 +464,7 @@ fn
policy_halts_skip_remaining_policies_and_restore_outer_state() {
impl ContextPolicy<Probe> for Stop {
fn default_visit(
&self,
- _: &VisitValue,
+ _: &StructuralView,
ctx: &mut VisitContext<'_, Probe>,
) -> Result<Option<VisitInterrupt>> {
assert_eq!(ctx.def_region_kind(), DefRegionKind::Pattern);
@@ -463,7 +481,7 @@ fn
policy_halts_skip_remaining_policies_and_restore_outer_state() {
impl ContextPolicy<Probe> for Unreachable {
fn default_visit(
&self,
- _: &VisitValue,
+ _: &StructuralView,
_: &mut VisitContext<'_, Probe>,
) -> Result<Option<VisitInterrupt>> {
panic!("the policy after Stop must not run")
@@ -475,12 +493,12 @@ fn
policy_halts_skip_remaining_policies_and_restore_outer_state() {
let policies = (Scope, (Stop(error), Unreachable));
let (result, state) = if let Some(order) = order {
let mut walker = WalkWithContextPolicy::new(Probe::default(),
policies);
- let result = walker.walk(&root, order);
+ let result = structural_walk(&root, &mut walker, order);
(result, walker.into_state())
} else {
let mut visitor = VisitCallbacks::new(
Probe::default(),
- |value: &VisitValue, ctx: &mut VisitContext<'_, Probe>| {
+ |value: &StructuralView, ctx: &mut VisitContext<'_,
Probe>| {
let kind = ctx.def_region_kind();
ctx.state_mut().walk_any(value, kind);
ctx.visit_children()
@@ -512,7 +530,7 @@ fn
policy_regions_compose_with_field_flags_and_function_hooks() {
impl ContextPolicy<PolicyRegionTrace> for SetRootRegion {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
ctx: &mut VisitContext<'_, PolicyRegionTrace>,
) -> Result<Option<VisitInterrupt>> {
if value.type_index() == self.0 {
@@ -551,7 +569,9 @@ fn
policy_regions_compose_with_field_flags_and_function_hooks() {
let state = if let Some(order) = order {
let mut walker =
WalkWithContextPolicy::new(PolicyRegionTrace::default(), policy);
- assert!(walker.walk(&root, order).unwrap().is_none());
+ assert!(structural_walk(&root, &mut walker, order)
+ .unwrap()
+ .is_none());
walker.into_state()
} else {
let mut visitor = VisitCallbacks::new(
@@ -578,7 +598,7 @@ fn
walk_policy_preserves_reflected_pattern_before_default_descent() {
impl ContextPolicy<PolicyRegionTrace> for Reenter {
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
ctx: &mut VisitContext<'_, PolicyRegionTrace>,
) -> Result<Option<VisitInterrupt>> {
match value.cast::<i64>() {
@@ -602,7 +622,9 @@ fn
walk_policy_preserves_reflected_pattern_before_default_descent() {
for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
let mut walker =
WalkWithContextPolicy::new(PolicyRegionTrace::default(),
Reenter(requested));
- assert!(walker.walk(&root, order).unwrap().is_none());
+ assert!(structural_walk(&root, &mut walker, order)
+ .unwrap()
+ .is_none());
let expected = match order {
WalkOrder::PreOrder => vec![
(1, Simple),
@@ -670,7 +692,7 @@ fn plain_walk_uses_registered_array_hook() {
let mut integers = 0;
assert!(structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<i64>().is_some() {
integers += 1;
}
@@ -692,7 +714,7 @@ fn plain_walk_visits_map_values_without_visiting_keys() {
let mut strings = 0;
assert!(structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<i64>().is_some() {
integers += 1;
} else if value.cast::<FfiString>().is_some() {
@@ -716,7 +738,7 @@ fn primitive_values_are_leaves_in_pre_and_post_order() {
let mut pre = Vec::new();
assert!(structural_walk(
&dtype,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<DLDataType>().is_some() {
pre.push("dtype");
} else if value.cast::<i64>().is_some() {
@@ -733,7 +755,7 @@ fn primitive_values_are_leaves_in_pre_and_post_order() {
let mut skipped = Vec::new();
assert!(structural_walk(
&dtype,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<DLDataType>().is_some() {
skipped.push("dtype");
WalkResult::Skip
@@ -751,7 +773,7 @@ fn primitive_values_are_leaves_in_pre_and_post_order() {
let mut post = Vec::new();
assert!(structural_walk(
&dtype,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<DLDataType>().is_some() {
post.push("dtype");
} else if value.cast::<i64>().is_some() {
@@ -771,7 +793,7 @@ fn primitive_fast_path_preserves_none_interrupt_and_error()
{
let mut none_calls = 0;
assert!(structural_walk(
&Any::new(),
- |_value: &VisitValue| {
+ |_value: &StructuralView| {
none_calls += 1;
WalkResult::Advance
},
@@ -811,7 +833,7 @@ fn
registered_map_hook_visits_all_values_without_visiting_keys() {
let mut strings = 0;
assert!(structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if let Some(integer) = value.cast::<i64>() {
sum += integer;
} else if value.cast::<FfiString>().is_some() {
@@ -834,7 +856,7 @@ fn interrupt_payload_crosses_map_traversal() {
.collect();
let outcome = structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<i64>().is_some() {
return WalkResult::interrupt_with(99i64);
}
@@ -854,7 +876,7 @@ fn handler_error_crosses_map_traversal() {
let root: Map<FfiString, i64> = [(FfiString::from("a"),
1i64)].into_iter().collect();
let error = match structural_walk(
&root,
- |value: &VisitValue| -> Result<WalkResult> {
+ |value: &StructuralView| -> Result<WalkResult> {
if value.cast::<i64>().is_some() {
Err(runtime_error("map handler failed"))
} else {
@@ -876,7 +898,7 @@ fn interrupt_stops_without_running_remaining_callbacks() {
let mut integers = 0;
let outcome = structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<i64>().is_some() {
integers += 1;
return WalkResult::Interrupt;
@@ -901,7 +923,7 @@ struct ManualRegionVisitor {
impl StructuralVisitor for ManualRegionVisitor {
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
if let Some(array) = value.cast::<Array<i64>>() {
@@ -1047,7 +1069,7 @@ struct StraddleVisitor {
impl StructuralVisitor for StraddleVisitor {
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let label = match value.cast::<i64>() {
@@ -1119,7 +1141,7 @@ fn nested_walk_restores_the_outer_active_visitor() {
assert!(structural_walk(
&outer,
- |value: &VisitValue| -> Result<WalkResult> {
+ |value: &StructuralView| -> Result<WalkResult> {
if let Some(value) = value.cast::<i64>() {
outer_values.push(value);
}
@@ -1127,7 +1149,7 @@ fn nested_walk_restores_the_outer_active_visitor() {
entered_inner = true;
structural_walk(
&inner,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if let Some(value) = value.cast::<i64>() {
inner_values.push(value);
}
@@ -1151,7 +1173,7 @@ fn interrupt_payload_is_returned_to_the_caller() {
let root = Array::new(vec![1i64, 2]);
let outcome = structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
if value.cast::<i64>() == Some(1) {
return WalkResult::interrupt_with(42i64);
}
@@ -1171,7 +1193,7 @@ fn handler_errors_include_native_visit_path() {
let root = Array::new(vec![1i64]);
let error = match structural_walk(
&root,
- |value: &VisitValue| -> Result<WalkResult> {
+ |value: &StructuralView| -> Result<WalkResult> {
if value.cast::<i64>().is_some() {
Err(runtime_error("handler failed"))
} else {
@@ -1194,7 +1216,7 @@ fn visitor_errors_include_native_visit_path() {
impl StructuralVisitor for FailingVisitor {
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
if value.cast::<i64>().is_some() {
@@ -1252,7 +1274,7 @@ fn
visitor_interrupt_propagates_through_default_children() {
impl StructuralVisitor for InterruptingVisitor {
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
if value.cast::<i64>() == Some(2) {
@@ -1280,7 +1302,7 @@ fn closure_walk_receives_def_region_kind() {
let mut kinds = Vec::new();
assert!(structural_walk(
&root,
- |value: &VisitValue, kind: DefRegionKind| {
+ |value: &StructuralView, kind: DefRegionKind| {
if value.cast::<i64>().is_some() {
kinds.push(kind);
}
@@ -1299,7 +1321,7 @@ fn closure_walk_supports_post_order_and_skip() {
let mut order_probe = Vec::new();
assert!(structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
order_probe.push(value.cast::<i64>());
WalkResult::Advance
},
@@ -1312,7 +1334,7 @@ fn closure_walk_supports_post_order_and_skip() {
let mut visited = 0;
assert!(structural_walk(
&root,
- |value: &VisitValue| {
+ |value: &StructuralView| {
visited += 1;
if value.cast::<i64>().is_none() {
WalkResult::Skip
@@ -1366,7 +1388,7 @@ fn chain_links_may_mix_def_region_arity() {
kinds.push(kind);
WalkResult::Advance
},
- |_value: &VisitValue, kind: DefRegionKind| {
+ |_value: &StructuralView, kind: DefRegionKind| {
assert_eq!(kind, DefRegionKind::None);
objects += 1;
WalkResult::Advance
@@ -1412,7 +1434,7 @@ fn chain_link_errors_include_native_visit_path() {
&root,
(
|_value: i64| -> Result<WalkResult> { Err(runtime_error("link
failed")) },
- |_value: &VisitValue| WalkResult::Advance,
+ |_value: &StructuralView| WalkResult::Advance,
),
WalkOrder::PreOrder,
) {
@@ -1504,7 +1526,7 @@ fn chain_supports_full_arity() {
objects += 1;
WalkResult::Advance
},
- |_value: &VisitValue, _kind: DefRegionKind| {
+ |_value: &StructuralView, _kind: DefRegionKind| {
others += 1;
WalkResult::Advance
},
@@ -1575,7 +1597,7 @@ struct InheritedRegionProbe {
impl StructuralVisitor for InheritedRegionProbe {
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
if self.at_root {
@@ -1681,12 +1703,12 @@ fn nested_tuple_chain_exceeds_flat_arity() {
objects += 1;
WalkResult::Advance
},
- (|_value: &VisitValue, _kind: DefRegionKind| {
+ (|_value: &StructuralView, _kind: DefRegionKind| {
others += 1;
WalkResult::Advance
},),
),
- |_value: &VisitValue| WalkResult::Advance,
+ |_value: &StructuralView| WalkResult::Advance,
),
),
WalkOrder::PreOrder,
@@ -1706,7 +1728,7 @@ fn nested_tuple_first_match_order_is_flattened() {
assert!(structural_walk(
&root,
(
- (|_value: &VisitValue| {
+ (|_value: &StructuralView| {
first += 1;
WalkResult::Advance
},),
@@ -1799,7 +1821,7 @@ fn
stateful_callback_visit_reborrows_visitor_during_recursion() {
let root = Array::new(vec![Array::new(vec![1i64, 2])]);
let mut visitor = VisitCallbacks::new(
StatefulVisitDepth::default(),
- |_value: &VisitValue, visitor: &mut VisitContext<'_,
StatefulVisitDepth>| {
+ |_value: &StructuralView, visitor: &mut VisitContext<'_,
StatefulVisitDepth>| {
visitor.state_mut().current += 1;
visitor.state_mut().calls += 1;
let current = visitor.state().current;
@@ -1823,7 +1845,7 @@ fn
callback_visit_can_reenter_the_same_fn_through_visitor() {
let visits = Cell::new(0);
assert!(structural_visit(
&root,
- |_value: &VisitValue, visitor: &mut VisitContext<'_, ()>| {
+ |_value: &StructuralView, visitor: &mut VisitContext<'_, ()>| {
visits.set(visits.get() + 1);
visitor.visit_children()
},
@@ -1843,7 +1865,7 @@ fn
callback_visit_tuple_is_first_match_and_can_interrupt() {
|value: i64, _visitor: &mut VisitContext<'_, ()>| {
(value == 2).then(|| VisitInterrupt::with(value))
},
- |_value: &VisitValue, visitor: &mut VisitContext<'_, ()>| {
+ |_value: &StructuralView, visitor: &mut VisitContext<'_, ()>| {
fallback.set(fallback.get() + 1);
visitor.visit_children()
},
@@ -1921,7 +1943,7 @@ fn
nested_callback_visit_restores_the_outer_active_visitor() {
assert!(structural_visit(
&outer,
- |value: &VisitValue, visitor: &mut VisitContext<'_, ()>| {
+ |value: &StructuralView, visitor: &mut VisitContext<'_, ()>| {
if let Some(value) = value.cast::<i64>() {
outer_values.borrow_mut().push(value);
}
diff --git a/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
b/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
index 6724e9bd..b69e1167 100644
--- a/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
+++ b/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
@@ -27,8 +27,8 @@
//! definition region, the rest inherit the surrounding state.
use tvm_ffi::{
- structural_visit, Array, DefRegionKind, Result, String as FfiString,
StructuralVisitor,
- VisitInterrupt, VisitValue,
+ structural_visit, Array, DefRegionKind, Result, String as FfiString,
StructuralView,
+ StructuralVisitor, VisitInterrupt,
};
/// C++: class TestVisitorObj : public StructuralVisitorObj
@@ -49,7 +49,7 @@ impl StructuralVisitor for RecordingVisitor {
/// `TFuncObj::StructuralVisit`.
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let integer = value.cast::<i64>();
@@ -103,9 +103,9 @@ fn records_values_and_def_region_modes() {
assert_eq!(
visitor.modes,
vec![
- DefRegionKind::None, // the array itself
+ DefRegionKind::None, // the array itself
DefRegionKind::Pattern, // element 0: the "params" position
- DefRegionKind::None, // element 1: the "body" position
+ DefRegionKind::None, // element 1: the "body" position
]
);
}