This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new 23eafe84 [FEAT][Rust] Preserve in-place mutation permissions through
callbacks (#798)
23eafe84 is described below
commit 23eafe84f57a3bbf1356a7e0fc9288be9ecc495f
Author: Shushi Hong <[email protected]>
AuthorDate: Thu Sep 17 17:57:20 2026 -0400
[FEAT][Rust] Preserve in-place mutation permissions through callbacks (#798)
Rust mutation callbacks previously used borrowed default recursion,
which could not reuse the original object's storage even when it was
uniquely owned.
This PR adds `MutateValue<T>` so callbacks can inspect a node, then pass
the handle by value to default recursion. The handle does not increase
the reference count. With `InplaceMode::Allow`, recursion can reuse
uniquely owned objects; shared objects are copied. `Disallow` always
prevents in-place modification.
Rust's borrow checker prevents a node reference borrowed from the handle
from remaining in use across its consumption.
Both `#[dispatch(mutate)]` handlers and closure callbacks support this
API, including helpers that preserve `UnchangedOr`. Callbacks now
inspect their argument directly and pass it explicitly to default
recursion; mutation contexts no longer expose `current()`.
---
docs/guides/rust_lang_guide.md | 41 +-
rust/tvm-ffi-macros/src/dispatch.rs | 106 ++-
rust/tvm-ffi/src/extra/dispatch.rs | 12 +-
rust/tvm-ffi/src/extra/mod.rs | 3 +
rust/tvm-ffi/src/extra/structural_common.rs | 22 +-
rust/tvm-ffi/src/extra/structural_mutate.rs | 775 ++++++++++++++++++----
rust/tvm-ffi/src/extra/structural_visit.rs | 132 ++--
rust/tvm-ffi/src/extra/structural_visit/policy.rs | 25 +-
rust/tvm-ffi/src/lib.rs | 7 +-
rust/tvm-ffi/tests/test_structural_mutate.rs | 389 ++++++++++-
rust/tvm-ffi/tests/test_structural_visit.rs | 2 +-
11 files changed, 1225 insertions(+), 289 deletions(-)
diff --git a/docs/guides/rust_lang_guide.md b/docs/guides/rust_lang_guide.md
index 2bc2764c..e308d685 100644
--- a/docs/guides/rust_lang_guide.md
+++ b/docs/guides/rust_lang_guide.md
@@ -270,7 +270,7 @@ order with the first matching argument type winning, like
the variadic C++
`StructuralWalk(root, callbacks...)` chain. A flat tuple holds up to 12
lambdas; a tuple is itself a link, so nest `(a, b, (c, d, ...))` to chain
more — order stays the flattened left-to-right order. Unmatched values
-simply advance; a `&VisitValue` lambda acts as a catch-all and must come
+simply advance; a `&StructuralView` lambda acts as a catch-all and must come
last, since links after an always-matching one never run. Each lambda may
take a trailing `DefRegionKind` argument:
@@ -339,7 +339,7 @@ Callbacks receive `VisitContext`, use first-match dispatch,
and own traversal
of matched values. `VisitCallbacks` adds state shared by the callback chain:
```rust
-use tvm_ffi::{structural_visit, Array, VisitCallbacks, VisitContext,
VisitValue};
+use tvm_ffi::{structural_visit, Array, VisitCallbacks, VisitContext,
StructuralView};
#[derive(Default)]
struct Stats {
@@ -353,7 +353,7 @@ let mut visitor = VisitCallbacks::new(
|value: i64, visitor: &mut VisitContext<'_, Stats>| {
visitor.state_mut().total += value;
},
- |_value: &VisitValue, visitor: &mut VisitContext<'_, Stats>| {
+ |_value: &StructuralView, visitor: &mut VisitContext<'_, Stats>| {
visitor.visit_children()
},
),
@@ -377,7 +377,7 @@ unmatched values use default child traversal:
```rust
use tvm_ffi::{
dispatch, structural_visit, Array, DefRegionKind, Result,
StructuralVisitor, VisitInterrupt,
- VisitValue,
+ StructuralView,
};
#[derive(Default)]
@@ -390,7 +390,7 @@ struct Depth {
impl Depth {
fn visit_any(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
self.current += 1;
@@ -417,18 +417,21 @@ than silently walked through reflection — visit such a
type's children
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.
+
### Structural Mapping and Mutation
`structural_map` is the transforming counterpart to `structural_walk`. Put
`#[dispatch(map)]` on an impl whose `map_*` methods return any value
convertible
into `Any`, directly or in `Result`. Methods are tested in source order, the
first matching argument type wins, and an unmatched value is preserved. A
-method may take an optional trailing `DefRegionKind`; a `&MapValue` method is a
+method may take an optional trailing `DefRegionKind`; a `&StructuralView`
method is a
catch-all and should therefore come last:
```rust
use tvm_ffi::{
- dispatch, structural_map, Any, Array, DefRegionKind, MapValue, Result,
WalkOrder,
+ dispatch, structural_map, Any, Array, DefRegionKind, StructuralView,
Result, WalkOrder,
};
#[derive(Default)]
@@ -443,7 +446,7 @@ impl Increment {
Ok(value + 1)
}
- fn map_other(&mut self, value: &MapValue) -> Any {
+ fn map_other(&mut self, value: &StructuralView) -> Any {
value.to_owned()
}
}
@@ -462,7 +465,7 @@ A single typed closure and an ordered tuple of up to twelve
closures are also
accepted; a tuple is itself a link, so `(a, b, (c, d, ...))` nests beyond that
without changing the flattened order. Tuple dispatch is first-match, not
broadcast: later closures do not
run after an earlier argument type matches. As with generated dispatch, a
-`&MapValue` catch-all belongs last. Numeric handlers claim the complete FFI
+`&StructuralView` catch-all belongs last. Numeric handlers claim the complete
FFI
`Int` or `Float` tag and then use Rust `as` conversion semantics; prefer `i64`
or `f64` unless narrowing is deliberate.
@@ -476,7 +479,7 @@ there is final. `Array` and `List` elements are mapped in
order. `Map` and
The root is consumed. A uniquely owned built-in container may reuse its
storage in place; passing `root.clone()` keeps the source shared and selects
copy-on-write behavior. The engine rechecks uniqueness after a pre-order
-callback, so retaining an owning `MapValue::to_owned()` alias before the
+callback, so retaining an owning `StructuralView::to_owned()` alias before the
container's children are mapped forces the non-in-place path. Reflected
objects must provide `__ffi_shallow_copy__`; the copy is validated before
fields are mapped and discarded if no structural field changes.
@@ -511,7 +514,7 @@ original value.
```rust
use tvm_ffi::{
- structural_mutate, Array, CallbackMutator, MapValue, MutateCallbacks,
+ structural_mutate, Array, CallbackMutator, MutateValue, MutateCallbacks,
};
#[derive(Default)]
@@ -526,8 +529,8 @@ let mut mutator = MutateCallbacks::new(
mutator.state_mut().integers += 1;
value + 1
},
- |_value: &MapValue, mutator: &mut CallbackMutator<Stats>| {
- mutator.default_mutate()
+ |value: MutateValue<'_>, mutator: &mut CallbackMutator<Stats>| {
+ mutator.default_maybe_inplace_mutate(value)
},
),
);
@@ -541,6 +544,12 @@ assert_eq!(mutator.state().integers, 2);
`maybe_inplace_mutate` preserves the reuse opportunity of an owned value.
Closure callbacks are `Fn`; mutable data belongs in the callback state.
+Use `MutateValue<'_, T>` with `default_maybe_inplace_mutate` to forward its
+permission, or `default_mutate_with_mode` to restrict it. Borrow through
`value`; mutation
+contexts do not expose `current()`, and copy-only default descent takes an
+explicit borrow (`default_mutate(value)`). Generated handlers may take
+`InplaceMode` after `&mut Mutator`; see `MutateValue` for ownership
requirements.
+
`#[dispatch(mutate)]` groups typed `mutate_*` callbacks. The dispatch object
owns its mutable pass state, while `Mutator` supplies recursion and the current
definition region. `mutator.mutate(self, child)` safely reborrows that dispatch
@@ -577,7 +586,7 @@ assert_eq!(increment.integers, 2);
For a named custom recursion policy, implement `StructuralMutator` and pass
`&mut` it to `structural_mutate`. `InplaceValue` is an engine-issued
-capability: callers cannot construct it from a read-only `MapValue`. Override
+capability: callers cannot construct it from a read-only `StructuralView`.
Override
`dispatch_maybe_inplace_mutate` to opt into default container reuse;
`default_maybe_inplace_mutate` rechecks uniqueness before writing. Borrowed
values can be re-entered with `mutate`, while owned values can use
@@ -585,7 +594,7 @@ values can be re-entered with `mutate`, while owned values
can use
```rust
use tvm_ffi::{
- structural_mutate, Any, Array, DefRegionKind, InplaceValue, MapValue,
Result,
+ structural_mutate, Any, Array, DefRegionKind, InplaceValue,
StructuralView, Result,
StructuralMutator,
};
@@ -593,7 +602,7 @@ use tvm_ffi::{
struct Increment;
impl StructuralMutator for Increment {
- fn dispatch_mutate(&mut self, value: &MapValue, kind: DefRegionKind) ->
Result<Any> {
+ fn dispatch_mutate(&mut self, value: &StructuralView, kind: DefRegionKind)
-> Result<Any> {
match value.cast::<i64>() {
Some(value) => Ok(Any::from(value + 1)),
None => self.default_mutate(value, kind),
diff --git a/rust/tvm-ffi-macros/src/dispatch.rs
b/rust/tvm-ffi-macros/src/dispatch.rs
index 85a16c86..feffa55b 100644
--- a/rust/tvm-ffi-macros/src/dispatch.rs
+++ b/rust/tvm-ffi-macros/src/dispatch.rs
@@ -71,13 +71,6 @@ impl DispatchMode {
}
}
- fn value_type(self) -> &'static str {
- match self {
- Self::Walk | Self::Visit => "VisitValue",
- Self::Map | Self::Mutate => "MapValue",
- }
- }
-
fn result_is_optional(self) -> bool {
match self {
Self::Walk | Self::Map | Self::Mutate => true,
@@ -182,7 +175,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
let span = handler.method.span();
let handler_attrs = &handler.cfg_attrs;
let later_attrs = &later.cfg_attrs;
- let value_type = mode.value_type();
+ let value_type = "StructuralView";
quote_spanned! {span=>
#(#[#impl_cfg_attrs])*
#(#[#handler_attrs])*
@@ -203,7 +196,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
#[allow(unreachable_code, unused_variables)]
fn dispatch_walk(
&mut self,
- value: &#tvm_ffi::extra::structural_visit::VisitValue,
+ value: &#tvm_ffi::StructuralView,
def_region_kind:
#tvm_ffi::extra::structural_visit::DefRegionKind,
) ->
Option<#tvm_ffi::extra::structural_visit::WalkCallbackResult> {
#(#links)*
@@ -219,7 +212,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
#[allow(unreachable_code, unused_variables)]
fn visit(
&mut self,
- value: &#tvm_ffi::extra::structural_visit::VisitValue,
+ value: &#tvm_ffi::StructuralView,
def_region_kind:
#tvm_ffi::extra::structural_visit::DefRegionKind,
) -> #tvm_ffi::Result<
Option<#tvm_ffi::extra::structural_visit::VisitInterrupt>
@@ -237,7 +230,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
#[allow(unreachable_code, unused_variables)]
fn dispatch_map(
&mut self,
- value: &#tvm_ffi::extra::structural_mutate::MapValue,
+ value: &#tvm_ffi::StructuralView,
def_region_kind:
#tvm_ffi::extra::structural_visit::DefRegionKind,
) -> Option<#tvm_ffi::extra::structural_mutate::MapResult> {
#(#links)*
@@ -253,9 +246,22 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) ->
syn::Result<TokenStream2>
#[allow(unreachable_code, unused_variables)]
fn dispatch_mutate(
&mut self,
- value: &#tvm_ffi::extra::structural_mutate::MapValue,
+ value: &#tvm_ffi::StructuralView,
mutator: &mut #tvm_ffi::extra::structural_mutate::Mutator,
) -> Option<#tvm_ffi::extra::structural_mutate::MutateResult> {
+ <Self as
#tvm_ffi::extra::structural_mutate::MutateDispatch>::dispatch_mutate_value(
+ self, #tvm_ffi::MutateValue::borrowed(value), mutator,
+ )
+ }
+
+ #[inline(always)]
+ #[allow(unreachable_code, unused_variables, unused_mut)]
+ fn dispatch_mutate_value(
+ &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();
#(#links)*
None
}
@@ -283,6 +289,9 @@ 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),
@@ -297,6 +306,11 @@ fn expand_links(
};
let invoke = match &handler.argument {
HandlerArgument::Value => {
+ let value = if matches!(mode, DispatchMode::Mutate) {
+ quote!(#value.as_value())
+ } else {
+ value.clone()
+ };
let result = wrap_result(quote! {
#into_result(self.#method(#value #trailing_arg))
});
@@ -314,6 +328,17 @@ fn expand_links(
}
}
}
+ HandlerArgument::Capability(value_type) => {
+ let result = wrap_result(quote! {
+ #into_result(self.#method(typed #trailing_arg))
+ });
+ quote! {
+ match #value.try_cast::<#value_type>() {
+ Ok(typed) => return #result,
+ Err(original) => #value = original,
+ }
+ }
+ }
HandlerArgument::Owned(value_type) => {
let result = wrap_result(quote! {
#into_result(self.#method(typed #trailing_arg))
@@ -340,6 +365,7 @@ struct Handler {
argument: HandlerArgument,
wants_def_region: bool,
wants_mutator: bool,
+ wants_inplace_mode: bool,
cfg_attrs: Vec<Meta>,
}
@@ -347,6 +373,7 @@ enum HandlerArgument {
Value,
BorrowedNode(Type),
Owned(Type),
+ Capability(Type),
}
fn parse_handler(method: &ImplItemMethod, mode: DispatchMode) ->
syn::Result<Handler> {
@@ -360,10 +387,12 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
}
_ => false,
};
- let arity_is_expected = inputs.len() == 2 || inputs.len() == 3;
+ let arity_is_expected = inputs.len() == 2
+ || inputs.len() == 3
+ || (matches!(mode, DispatchMode::Mutate) && inputs.len() == 4);
if !receiver_is_expected || !arity_is_expected {
let message = if matches!(mode, DispatchMode::Mutate) {
- "mutate handlers must take `&mut self`, a node, and optionally
`&mut Mutator`"
+ "mutate handlers must take `&mut self`, a node, optionally `&mut
Mutator`, then optionally `InplaceMode`"
.to_owned()
} else {
format!(
@@ -375,7 +404,17 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
return Err(syn::Error::new_spanned(&method.sig, message));
}
let wants_def_region = !matches!(mode, DispatchMode::Mutate) &&
inputs.len() == 3;
- let wants_mutator = 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(),
@@ -390,7 +429,7 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
};
let argument = match &value_type {
Type::Reference(reference) if reference.mutability.is_none() => {
- if is_dispatch_value(reference.elem.as_ref(), mode) {
+ if is_dispatch_value(reference.elem.as_ref()) {
HandlerArgument::Value
} else {
HandlerArgument::BorrowedNode((*reference.elem).clone())
@@ -405,6 +444,28 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
),
));
}
+ Type::Path(path)
+ if matches!(mode, DispatchMode::Mutate)
+ && path
+ .path
+ .segments
+ .last()
+ .is_some_and(|s| s.ident == "MutateValue") =>
+ {
+ let segment = path.path.segments.last().unwrap();
+ let value_type = if let PathArguments::AngleBracketed(args) =
&segment.arguments {
+ args.args.iter().find_map(|arg| match arg {
+ syn::GenericArgument::Type(ty) => Some(ty.clone()),
+ _ => None,
+ })
+ } else {
+ None
+ };
+ let tvm_ffi = get_tvm_ffi_crate();
+ HandlerArgument::Capability(
+ value_type.unwrap_or_else(|| syn::parse_quote!(#tvm_ffi::Any)),
+ )
+ }
_ => HandlerArgument::Owned(value_type),
};
let cfg_attrs = presence_attrs(&method.attrs)?;
@@ -413,6 +474,7 @@ fn parse_handler(method: &ImplItemMethod, mode:
DispatchMode) -> syn::Result<Han
argument,
wants_def_region,
wants_mutator,
+ wants_inplace_mode,
cfg_attrs,
})
}
@@ -496,12 +558,14 @@ fn presence_meta(meta: Meta) -> Option<Meta> {
}
}
-fn is_dispatch_value(value_type: &Type, mode: DispatchMode) -> bool {
+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| segment.ident == mode.value_type())
+ path.path.segments.last().is_some_and(|segment| {
+ matches!(
+ segment.ident.to_string().as_str(),
+ "StructuralView" | "VisitValue" | "MapValue"
+ )
+ })
}
diff --git a/rust/tvm-ffi/src/extra/dispatch.rs
b/rust/tvm-ffi/src/extra/dispatch.rs
index 314965fa..af6f2a8e 100644
--- a/rust/tvm-ffi/src/extra/dispatch.rs
+++ b/rust/tvm-ffi/src/extra/dispatch.rs
@@ -22,7 +22,7 @@
use crate::error::Result;
use super::structural_visit::{
- DefRegionKind, IntoWalker, NativeVisit, VisitValue, WalkCallbackResult,
WalkResult,
+ DefRegionKind, IntoWalker, NativeVisit, StructuralView,
WalkCallbackResult, WalkResult,
};
/// Dispatch for typed `structural_walk` observer callbacks.
@@ -32,7 +32,7 @@ use super::structural_visit::{
pub trait WalkDispatch: Sized {
fn dispatch_walk(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult>;
}
@@ -41,7 +41,7 @@ impl<V: WalkDispatch> WalkDispatch for &mut V {
#[inline]
fn dispatch_walk(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
(**self).dispatch_walk(value, def_region_kind)
@@ -65,7 +65,11 @@ pub struct DispatchWalker<V> {
}
impl<V: WalkDispatch> NativeVisit for DispatchWalker<V> {
- fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) ->
Result<WalkResult> {
+ fn visit(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<WalkResult> {
self.walker
.dispatch_walk(value, def_region_kind)
.unwrap_or(Ok(WalkResult::Advance))
diff --git a/rust/tvm-ffi/src/extra/mod.rs b/rust/tvm-ffi/src/extra/mod.rs
index 3abc1702..49963753 100644
--- a/rust/tvm-ffi/src/extra/mod.rs
+++ b/rust/tvm-ffi/src/extra/mod.rs
@@ -22,3 +22,6 @@ mod structural_common;
pub mod structural_mutate;
pub mod structural_visit;
pub mod unchanged;
+
+/// Borrowed value shared by structural traversal and transformation callbacks.
+pub use structural_common::StructuralView;
diff --git a/rust/tvm-ffi/src/extra/structural_common.rs
b/rust/tvm-ffi/src/extra/structural_common.rs
index be87a044..73242c84 100644
--- a/rust/tvm-ffi/src/extra/structural_common.rs
+++ b/rust/tvm-ffi/src/extra/structural_common.rs
@@ -64,15 +64,25 @@ macro_rules! impl_callback_chain_tuple_arities {
pub(crate) use impl_callback_chain_tuple_arities;
-/// A borrowed value shared by structural visit and map callbacks.
+/// A borrowed value shared by structural visit, walk, map, and mutate
callbacks.
///
-/// This type centralizes the audited unsafe operations used to cast FFI
-/// values and borrow object nodes. The public APIs expose it as `VisitValue`
-/// or `MapValue` according to the callback context.
+/// Use `&StructuralView` for an erased callback argument. [`Self::as_node`]
+/// 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 StructuralValue(TVMFFIAny);
+pub struct StructuralView(TVMFFIAny);
-impl StructuralValue {
+impl<'a> From<&'a StructuralView> for AnyView<'a> {
+ #[inline]
+ fn from(value: &'a StructuralView) -> Self {
+ // SAFETY: the view cannot outlive the borrowed structural value.
+ unsafe { AnyView::from_raw_ffi_any(value.raw()) }
+ }
+}
+
+impl StructuralView {
#[inline]
pub(crate) fn from_raw(raw: TVMFFIAny) -> Self {
Self(raw)
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 96bcba4b..80998f0a 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -66,7 +66,10 @@ const FLAG_SEQ_HASH_IGNORE: i64 =
kTVMFFIFieldFlagBitMaskSEqHashIgnore as i64;
const FLAG_SETTER_IS_FUNCTION: i64 = kTVMFFIFieldFlagBitSetterIsFunctionObj as
i64;
/// Borrowed value passed to structural map and mutation callbacks.
-pub use super::structural_common::StructuralValue as MapValue;
+pub use super::StructuralView;
+
+/// Compatibility name for [`StructuralView`].
+pub use super::StructuralView as MapValue;
/// Result type produced by a structural-map callback.
#[doc(hidden)]
@@ -106,14 +109,161 @@ impl<T: Into<Any>> IntoMapResult for Result<T> {
}
}
+/// Permission to attempt in-place structural mutation along an owned path.
+///
+/// `Allow` still requires unique ownership; it does not grant permission to
+/// modify a borrowed value. Use an owned value or an engine-issued
+/// [`InplaceValue`] with the mode-aware mutation helpers.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub enum InplaceMode {
+ /// Use ordinary mutation, including for uniquely owned values.
+ #[default]
+ Disallow,
+ /// Permit reuse when ownership and alias checks allow it.
+ Allow,
+}
+
+impl InplaceMode {
+ fn permit(self) -> Permit {
+ match self {
+ Self::Disallow => Permit::Copy,
+ Self::Allow => Permit::MaybeInPlace,
+ }
+ }
+
+ fn permit_if_unique(self, raw: TVMFFIAny) -> Permit {
+ if self == Self::Allow && object_is_unique(raw) {
+ Permit::MaybeInPlace
+ } else {
+ Permit::Copy
+ }
+ }
+}
+
+/// A callback value carrying the engine's in-place permission.
+///
+/// Unlike an owning typed argument, this handle does not increment the
reference
+/// count. Borrow through it to inspect the node, then consume it in
+/// `default_maybe_inplace_mutate` to continue recursion. A surviving owning
alias
+/// still forces copying. Node borrows come from this handle, not from the
+/// callback context, so the handle can safely be passed to another context.
+/// `T` selects the callback's matched FFI type; `Any` matches every value.
+///
+/// A node borrow cannot survive consumption of the handle:
+/// ```compile_fail
+/// use tvm_ffi::{CallbackMutator, MutateValue};
+/// fn invalid(value: MutateValue<'_>, ctx: &mut CallbackMutator) {
+/// let node =
value.as_node::<tvm_ffi::collections::array::ArrayObj>().unwrap();
+/// ctx.default_maybe_inplace_mutate(value).unwrap();
+/// println!("{}", node.size);
+/// }
+/// ```
+///
+/// Moving the handle into a nested callback cannot bypass that borrow:
+/// ```compile_fail
+/// use std::cell::RefCell;
+/// use tvm_ffi::{structural_mutate, CallbackMutator, 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| {
+///
inner.default_maybe_inplace_mutate(pending.borrow_mut().take().unwrap())
+/// }).unwrap();
+/// println!("{}", node.size);
+/// }
+/// ```
+pub struct MutateValue<'a, T = Any> {
+ value: StructuralView,
+ mode: InplaceMode,
+ _scope: PhantomData<&'a StructuralView>,
+ _type: PhantomData<fn() -> T>,
+ _not_send_sync: PhantomData<Rc<()>>,
+}
+
+impl<'a> MutateValue<'a> {
+ fn new(value: &'a StructuralView, mode: InplaceMode) -> Self {
+ Self {
+ value: StructuralView::from_raw(value.raw()),
+ mode,
+ _scope: PhantomData,
+ _type: PhantomData,
+ _not_send_sync: PhantomData,
+ }
+ }
+
+ /// Wrap a borrowed value without granting in-place permission.
+ pub fn borrowed(value: &'a StructuralView) -> Self {
+ Self::new(value, InplaceMode::Disallow)
+ }
+}
+
+impl<'a, T> MutateValue<'a, T> {
+ /// Permission established before callback arguments acquire ownership.
+ pub fn inplace_mode(&self) -> InplaceMode {
+ self.mode
+ }
+
+ /// Borrow the current value. The borrow must end before default mutation.
+ pub fn as_value(&self) -> &StructuralView {
+ &self.value
+ }
+
+ /// Narrow the matched type without acquiring an owning reference.
+ pub fn try_cast<U: crate::type_traits::ContainerElement>(
+ self,
+ ) -> std::result::Result<MutateValue<'a, U>, Self> {
+ // SAFETY: the engine keeps the borrowed value live for this handle's
+ // lifetime. This only checks its type and does not acquire ownership.
+ if unsafe { U::container_check_any_strict(&self.value.raw()) } {
+ Ok(MutateValue {
+ value: self.value,
+ mode: self.mode,
+ _scope: PhantomData,
+ _type: PhantomData,
+ _not_send_sync: PhantomData,
+ })
+ } else {
+ Err(self)
+ }
+ }
+
+ /// Erase the matched type while preserving the engine-issued permission.
+ pub fn into_untyped(self) -> MutateValue<'a> {
+ MutateValue {
+ value: self.value,
+ mode: self.mode,
+ _scope: PhantomData,
+ _type: PhantomData,
+ _not_send_sync: PhantomData,
+ }
+ }
+
+ fn permit(&self, requested: InplaceMode) -> Permit {
+ if self.mode == InplaceMode::Disallow {
+ Permit::Copy
+ } else {
+ requested.permit_if_unique(self.value.raw())
+ }
+ }
+}
+
+impl<T> Deref for MutateValue<'_, T> {
+ type Target = StructuralView;
+ fn deref(&self) -> &Self::Target {
+ self.as_value()
+ }
+}
+
/// State and recursive operations available to a callback-chain mutation.
///
/// 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,
- current: MapValue,
def_region_kind: DefRegionKind,
+ inplace_mode: InplaceMode,
_state: PhantomData<fn() -> State>,
_not_send_sync: PhantomData<Rc<()>>,
}
@@ -129,18 +279,21 @@ pub type CallbackMutator<'a, State = (), Driver = dyn
MutateContextDriver<State>
///
/// The dispatch object owns all pass state. Recursive operations take that
/// object explicitly so Rust can safely reborrow the same `&mut self` for the
-/// child call.
+/// child call. Node access comes only from the callback argument; default
+/// recursion takes that value explicitly.
pub struct Mutator {
- current: MapValue,
def_region_kind: DefRegionKind,
+ inplace_mode: InplaceMode,
_not_send_sync: PhantomData<Rc<()>>,
}
impl Mutator {
- /// Complete borrowed value active at this callback.
- #[inline(always)]
- pub fn current(&self) -> &MapValue {
- &self.current
+ /// Engine-established permission for this callback's current value.
+ ///
+ /// This is not authority to modify a borrow: default in-place descent also
+ /// requires consuming a [`MutateValue`].
+ pub fn inplace_mode(&self) -> InplaceMode {
+ self.inplace_mode
}
/// Definition-region state active at the callback's current value.
@@ -202,13 +355,35 @@ impl Mutator {
D: MutateDispatch,
T: Into<Any>,
{
- StructuralMutator::maybe_inplace_mutate(dispatch, value,
def_region_kind)
+ self.maybe_inplace_mutate_with_mode(dispatch, value, def_region_kind,
InplaceMode::Allow)
}
- /// Apply default mutation to the callback's current value.
+ /// Mutate an owned child under an explicit region and in-place permission.
+ ///
+ /// `Disallow` keeps this input on the copy path even when uniquely owned.
+ #[inline(always)]
+ pub fn maybe_inplace_mutate_with_mode<D, T>(
+ &mut self,
+ dispatch: &mut D,
+ value: T,
+ def_region_kind: DefRegionKind,
+ mode: InplaceMode,
+ ) -> Result<Any>
+ where
+ D: MutateDispatch,
+ T: Into<Any>,
+ {
+ StructuralMutator::maybe_inplace_mutate_with_mode(dispatch, value,
def_region_kind, mode)
+ }
+
+ /// Apply default copy-only mutation to an explicitly borrowed value.
#[inline(always)]
- pub fn default_mutate<D: MutateDispatch>(&mut self, dispatch: &mut D) ->
Result<Any> {
- StructuralMutator::default_mutate(dispatch, &self.current,
self.def_region_kind)
+ pub fn default_mutate<D, T>(&mut self, dispatch: &mut D, value: &T) ->
Result<Any>
+ where
+ D: MutateDispatch,
+ for<'x> AnyView<'x>: From<&'x T>,
+ {
+ StructuralMutator::default_mutate_value(dispatch, value,
self.def_region_kind)
}
/// Mutate a borrowed child while preserving an unchanged result.
@@ -238,11 +413,66 @@ impl Mutator {
/// Apply default mutation without materializing an unchanged original.
#[inline]
- pub fn default_mutate_result<D: MutateDispatch>(
+ pub fn default_mutate_result<D, T>(
&mut self,
dispatch: &mut D,
+ value: &T,
+ ) -> Result<UnchangedOr<Any>>
+ where
+ D: MutateDispatch,
+ for<'x> AnyView<'x>: From<&'x T>,
+ {
+ StructuralMutator::default_mutate_value_result(dispatch, value,
self.def_region_kind)
+ }
+
+ /// Consume the handle for default descent with its existing permission.
+ #[inline]
+ pub fn default_maybe_inplace_mutate<D: MutateDispatch, T>(
+ &mut self,
+ dispatch: &mut D,
+ value: MutateValue<'_, T>,
+ ) -> Result<Any> {
+ let mode = value.inplace_mode();
+ self.default_mutate_with_mode(dispatch, value, mode)
+ }
+
+ /// Consume the handle for default descent, preserving permission and
`Unchanged`.
+ #[inline]
+ pub fn default_maybe_inplace_mutate_result<D: MutateDispatch, T>(
+ &mut self,
+ dispatch: &mut D,
+ value: MutateValue<'_, T>,
+ ) -> Result<UnchangedOr<Any>> {
+ let mode = value.inplace_mode();
+ self.default_mutate_with_mode_result(dispatch, value, mode)
+ }
+
+ /// Continue default mutation after relinquishing the callback value's
borrows.
+ ///
+ /// The requested mode can restrict, but cannot upgrade, the engine-issued
+ /// permission. Retained owning aliases force copying.
+ pub fn default_mutate_with_mode<D: MutateDispatch, T>(
+ &mut self,
+ dispatch: &mut D,
+ value: MutateValue<'_, T>,
+ mode: InplaceMode,
+ ) -> Result<Any> {
+ let raw = value.value.raw();
+ let permit = value.permit(mode);
+ user_default_mutate(dispatch, raw, self.def_region_kind, permit)
+ .and_then(|result| resolve_result(result, raw))
+ }
+
+ /// Consume the callback value for default descent, preserving `Unchanged`.
+ pub fn default_mutate_with_mode_result<D: MutateDispatch, T>(
+ &mut self,
+ dispatch: &mut D,
+ value: MutateValue<'_, T>,
+ mode: InplaceMode,
) -> Result<UnchangedOr<Any>> {
- StructuralMutator::default_mutate_result(dispatch, &self.current,
self.def_region_kind)
+ let permit = value.permit(mode);
+ user_default_mutate(dispatch, value.value.raw(), self.def_region_kind,
permit)
+ .and_then(UnchangedOr::from_carrier)
}
/// Look up an invocation-local identity substitution.
@@ -250,7 +480,7 @@ impl Mutator {
pub fn var_remap_get<D: MutateDispatch>(
&mut self,
dispatch: &mut D,
- var: &MapValue,
+ var: &StructuralView,
) -> Result<Option<Any>> {
StructuralMutator::var_remap_get(dispatch, var)
}
@@ -260,7 +490,7 @@ impl Mutator {
pub fn var_remap_set<D: MutateDispatch>(
&mut self,
dispatch: &mut D,
- var: &MapValue,
+ var: &StructuralView,
mutated_value: &Any,
) -> Result<()> {
StructuralMutator::var_remap_set(dispatch, var, mutated_value)
@@ -272,20 +502,25 @@ impl Mutator {
///
/// 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.
+/// point. 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> {
fn state(&self) -> &State;
fn state_mut(&mut self) -> &mut State;
- fn mutate_raw(
+ fn mutate_borrowed(&mut self, value: AnyView<'_>, kind: DefRegionKind) ->
Result<Any>;
+ fn mutate_owned(&mut self, value: Any, kind: DefRegionKind, mode:
InplaceMode) -> Result<Any>;
+ fn default_mutate_borrowed(&mut self, value: AnyView<'_>, kind:
DefRegionKind) -> Result<Any>;
+ /// Consume the handle to exclude node borrows before default descent.
+ fn default_mutate_value(
&mut self,
- raw: TVMFFIAny,
- def_region_kind: DefRegionKind,
- permit: Permit,
- ) -> Result<Any>;
- fn default_mutate_raw(&mut self, raw: TVMFFIAny, def_region_kind:
DefRegionKind)
- -> Result<Any>;
- fn var_remap_get_raw(&mut self, raw: TVMFFIAny) -> Result<Option<Any>>;
- fn var_remap_set_raw(&mut self, raw: TVMFFIAny, mutated_value: &Any) ->
Result<()>;
+ value: MutateValue<'_>,
+ kind: DefRegionKind,
+ _mode: InplaceMode,
+ ) -> Result<Any> {
+ self.default_mutate_borrowed(AnyView::from(value.as_value()), kind)
+ }
+ fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>>;
+ fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()>;
}
impl<State, Driver> MutateContext<'_, State, Driver>
@@ -304,10 +539,12 @@ where
self.driver.state_mut()
}
- /// Complete borrowed value active at this callback.
- #[inline(always)]
- pub fn current(&self) -> &MapValue {
- &self.current
+ /// Engine-established permission for this callback's current value.
+ ///
+ /// This is not authority to modify a borrow: default in-place descent also
+ /// requires consuming a [`MutateValue`].
+ pub fn inplace_mode(&self) -> InplaceMode {
+ self.inplace_mode
}
/// Definition-region state active at the callback's current value.
@@ -340,7 +577,7 @@ where
{
let view = AnyView::from(value);
self.driver
- .mutate_raw(*view.as_raw_ffi_any(), def_region_kind, Permit::Copy)
+ .mutate_borrowed(view, def_region_kind)
.and_then(|result| resolve_result(result, *view.as_raw_ffi_any()))
}
@@ -358,24 +595,37 @@ where
value: T,
def_region_kind: DefRegionKind,
) -> Result<Any> {
- let value = value.into();
- let result = self.driver.mutate_raw(
- *value.as_raw_ffi_any(),
- def_region_kind,
- Permit::MaybeInPlace,
- )?;
- Ok(if is_unchanged(&result) { value } else { result })
+ self.maybe_inplace_mutate_with_mode(value, def_region_kind,
InplaceMode::Allow)
}
- /// Apply default mutation to the callback's current value.
+ /// Mutate an owned value under an explicit region and in-place permission.
///
- /// This operation always uses the copy path because a callback may still
- /// hold a shared borrow of the current value. It may be called repeatedly.
+ /// `Disallow` keeps this input on the copy path even when uniquely owned.
#[inline(always)]
- pub fn default_mutate(&mut self) -> Result<Any> {
+ pub fn maybe_inplace_mutate_with_mode<T: Into<Any>>(
+ &mut self,
+ value: T,
+ def_region_kind: DefRegionKind,
+ mode: InplaceMode,
+ ) -> Result<Any> {
self.driver
- .default_mutate_raw(self.current.raw(), self.def_region_kind)
- .and_then(|result| resolve_result(result, self.current.raw()))
+ .mutate_owned(value.into(), def_region_kind, mode)
+ }
+
+ /// Apply default copy-only mutation to an explicitly borrowed value.
+ ///
+ /// This may be called repeatedly while holding node borrows. The value's
+ /// children re-enter callback dispatch, but the value itself does not.
+ #[inline(always)]
+ pub fn default_mutate<T>(&mut self, value: &T) -> Result<Any>
+ where
+ for<'x> AnyView<'x>: From<&'x T>,
+ {
+ let view = AnyView::from(value);
+ let raw = *view.as_raw_ffi_any();
+ self.driver
+ .default_mutate_borrowed(view, self.def_region_kind)
+ .and_then(|result| resolve_result(result, raw))
}
/// Mutate a borrowed child while preserving an unchanged result.
@@ -399,28 +649,76 @@ where
{
let view = AnyView::from(value);
self.driver
- .mutate_raw(*view.as_raw_ffi_any(), kind, Permit::Copy)
+ .mutate_borrowed(view, kind)
.and_then(UnchangedOr::from_carrier)
}
/// Apply default mutation without materializing an unchanged original.
#[inline]
- pub fn default_mutate_result(&mut self) -> Result<UnchangedOr<Any>> {
+ pub fn default_mutate_result<T>(&mut self, value: &T) ->
Result<UnchangedOr<Any>>
+ where
+ for<'x> AnyView<'x>: From<&'x T>,
+ {
+ let view = AnyView::from(value);
+ self.driver
+ .default_mutate_borrowed(view, self.def_region_kind)
+ .and_then(UnchangedOr::from_carrier)
+ }
+
+ /// Consume the handle for default descent with its existing permission.
+ #[inline]
+ pub fn default_maybe_inplace_mutate<T>(&mut self, value: MutateValue<'_,
T>) -> Result<Any> {
+ let mode = value.inplace_mode();
+ self.default_mutate_with_mode(value, mode)
+ }
+
+ /// Consume the handle for default descent, preserving permission and
`Unchanged`.
+ #[inline]
+ pub fn default_maybe_inplace_mutate_result<T>(
+ &mut self,
+ value: MutateValue<'_, T>,
+ ) -> Result<UnchangedOr<Any>> {
+ let mode = value.inplace_mode();
+ self.default_mutate_with_mode_result(value, mode)
+ }
+
+ /// Continue default mutation after relinquishing the callback value's
borrows.
+ ///
+ /// The mode can restrict, but cannot upgrade, the handle's permission.
+ /// Retained owning aliases force copying; temporary handles can be dropped
+ /// before this call to preserve reuse of the current value.
+ pub fn default_mutate_with_mode<T>(
+ &mut self,
+ value: MutateValue<'_, T>,
+ mode: InplaceMode,
+ ) -> Result<Any> {
+ let raw = value.value.raw();
+ self.driver
+ .default_mutate_value(value.into_untyped(), self.def_region_kind,
mode)
+ .and_then(|result| resolve_result(result, raw))
+ }
+
+ /// Consume the callback value for default descent, preserving `Unchanged`.
+ pub fn default_mutate_with_mode_result<T>(
+ &mut self,
+ value: MutateValue<'_, T>,
+ mode: InplaceMode,
+ ) -> Result<UnchangedOr<Any>> {
self.driver
- .default_mutate_raw(self.current.raw(), self.def_region_kind)
+ .default_mutate_value(value.into_untyped(), self.def_region_kind,
mode)
.and_then(UnchangedOr::from_carrier)
}
/// Look up an invocation-local identity substitution.
#[inline(always)]
- pub fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
- self.driver.var_remap_get_raw(var.raw())
+ pub fn var_remap_get(&mut self, var: &StructuralView) ->
Result<Option<Any>> {
+ self.driver.var_remap_get(var)
}
/// Store an invocation-local identity substitution.
#[inline(always)]
- pub fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) ->
Result<()> {
- self.driver.var_remap_set_raw(var.raw(), mutated_value)
+ pub fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any)
-> Result<()> {
+ self.driver.var_remap_set(var, mutated_value)
}
}
@@ -432,7 +730,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 `&MapValue`, 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 CallbackMutator<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> {
@@ -484,7 +782,7 @@ pub trait MutateChainLink<State, Marker>:
mutate_sealed::SealedLink<State, Marke
#[doc(hidden)]
fn try_mutate(
&self,
- value: &MapValue,
+ value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
) -> Option<MutateResult>;
}
@@ -495,9 +793,25 @@ pub trait MutateChainLink<State, Marker>:
mutate_sealed::SealedLink<State, Marke
/// behavior. A generated `#[dispatch(mutate)]` implementation tests
/// `mutate_*` methods in source order and passes the same [`Mutator`] to the
/// first match. The implementation owns its pass state and receives `&mut
-/// self`, while [`Mutator`] only controls recursion and the definition region.
+/// 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`.
pub trait MutateDispatch: Sized {
- fn dispatch_mutate(&mut self, value: &MapValue, mutator: &mut Mutator) ->
Option<MutateResult>;
+ fn dispatch_mutate(
+ &mut self,
+ value: &StructuralView,
+ mutator: &mut Mutator,
+ ) -> Option<MutateResult>;
+
+ /// Dispatch an engine-issued value. Existing borrowed dispatchers retain
+ /// their copy-only default recursion; generated dispatch supports
consuming it.
+ fn dispatch_mutate_value(
+ &mut self,
+ value: MutateValue<'_>,
+ mutator: &mut Mutator,
+ ) -> Option<MutateResult> {
+ self.dispatch_mutate(value.as_value(), mutator)
+ }
}
impl<D: MutateDispatch> IntoMutator<ByMutateDispatch> for D {
@@ -508,10 +822,20 @@ impl<D: MutateDispatch> IntoMutator<ByMutateDispatch> for
D {
}
mod mutate_sealed {
- use super::{IntoMutateResult, MapValue, MutateContext, ObjectCore};
+ use super::{IntoMutateResult, MutateContext, MutateValue, ObjectCore,
StructuralView};
pub trait SealedLink<State, Marker> {}
+ impl<F, State, T, O> SealedLink<State, super::ByMutateValue<T>> for F
+ where
+ F: for<'value, 'mutator, 'driver> Fn(
+ MutateValue<'value, T>,
+ &'mutator mut MutateContext<'driver, State>,
+ ) -> O,
+ O: IntoMutateResult,
+ {
+ }
+
impl<F, State, T, O> SealedLink<State, super::ByMutateOwned<T>> for F
where
F: for<'mutator, 'driver> Fn(T, &'mutator mut MutateContext<'driver,
State>) -> O,
@@ -532,7 +856,7 @@ mod mutate_sealed {
impl<F, State, O> SealedLink<State, super::ByMutateCatchAll> for F
where
F: for<'value, 'mutator, 'driver> Fn(
- &'value MapValue,
+ &'value StructuralView,
&'mutator mut MutateContext<'driver, State>,
) -> O,
O: IntoMutateResult,
@@ -543,6 +867,38 @@ mod mutate_sealed {
#[doc(hidden)]
pub enum ByMutateDispatch {}
+#[doc(hidden)]
+pub struct ByMutateValue<T>(PhantomData<T>);
+
+impl<F, State, T, O> MutateChainLink<State, ByMutateValue<T>> for F
+where
+ F: for<'value, 'mutator, 'driver> Fn(
+ MutateValue<'value, T>,
+ &'mutator mut MutateContext<'driver, State>,
+ ) -> O,
+ T: crate::type_traits::ContainerElement,
+ O: IntoMutateResult,
+{
+ type Strategy = DynamicMutateCallbacks;
+ fn try_mutate(
+ &self,
+ value: &mut Option<MutateValue<'_>>,
+ mutator: &mut MutateContext<'_, State>,
+ ) -> Option<MutateResult> {
+ match value
+ .take()
+ .expect("unconsumed callback value")
+ .try_cast::<T>()
+ {
+ Ok(typed) => Some(self(typed, mutator).into_mutate_result()),
+ Err(original) => {
+ *value = Some(original);
+ None
+ }
+ }
+ }
+}
+
#[doc(hidden)]
pub struct ByMutateOwned<T>(PhantomData<T>);
@@ -556,10 +912,12 @@ where
fn try_mutate(
&self,
- value: &MapValue,
+ value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
) -> Option<MutateResult> {
value
+ .as_ref()
+ .expect("unconsumed callback value")
.cast::<T>()
.map(|typed| self(typed, mutator).into_mutate_result())
}
@@ -581,10 +939,12 @@ where
fn try_mutate(
&self,
- value: &MapValue,
+ value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
) -> Option<MutateResult> {
value
+ .as_ref()
+ .expect("unconsumed callback value")
.as_node::<N>()
.map(|node| self(node, mutator).into_mutate_result())
}
@@ -596,7 +956,7 @@ pub enum ByMutateCatchAll {}
impl<F, State, O> MutateChainLink<State, ByMutateCatchAll> for F
where
F: for<'value, 'mutator, 'driver> Fn(
- &'value MapValue,
+ &'value StructuralView,
&'mutator mut MutateContext<'driver, State>,
) -> O,
O: IntoMutateResult,
@@ -605,10 +965,19 @@ where
fn try_mutate(
&self,
- value: &MapValue,
+ value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
) -> Option<MutateResult> {
- Some(self(value, mutator).into_mutate_result())
+ Some(
+ self(
+ value
+ .as_ref()
+ .expect("unconsumed callback value")
+ .as_value(),
+ mutator,
+ )
+ .into_mutate_result(),
+ )
}
}
@@ -633,7 +1002,7 @@ macro_rules! impl_mutate_chain_link {
fn try_mutate(
&self,
- value: &MapValue,
+ value: &mut Option<MutateValue<'_>>,
mutator: &mut MutateContext<'_, State>,
) -> Option<MutateResult> {
$(
@@ -745,7 +1114,7 @@ where
pub trait MapDispatch: Sized {
fn dispatch_map(
&mut self,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<MapResult>;
}
@@ -754,7 +1123,7 @@ impl<V: MapDispatch> MapDispatch for &mut V {
#[inline]
fn dispatch_map(
&mut self,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<MapResult> {
(**self).dispatch_map(value, def_region_kind)
@@ -787,14 +1156,18 @@ impl<'a, V: MapDispatch> IntoMapper<ByMapDispatch> for
&'a mut V {
/// One typed callback in a structural-map tuple.
///
/// Links use first-match order and may receive an owned FFI value, borrowed
-/// object node, or `&MapValue`, optionally followed by [`DefRegionKind`].
+/// object node, or `&StructuralView`, optionally followed by
[`DefRegionKind`].
pub trait MapChainLink<Marker>: sealed_map::SealedMapLink<Marker> {
#[doc(hidden)]
- fn try_map(&mut self, value: &MapValue, def_region_kind: DefRegionKind) ->
Option<MapResult>;
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Option<MapResult>;
}
mod sealed_map {
- use super::{DefRegionKind, IntoMapResult, MapDispatch, MapValue,
ObjectCore};
+ use super::{DefRegionKind, IntoMapResult, MapDispatch, ObjectCore,
StructuralView};
pub trait SealedMapLink<Marker> {}
@@ -828,14 +1201,14 @@ mod sealed_map {
impl<F, O> SealedMapLink<super::ByMapCatchAll> for F
where
- F: for<'a> FnMut(&'a MapValue) -> O,
+ F: for<'a> FnMut(&'a StructuralView) -> O,
O: IntoMapResult,
{
}
impl<F, O> SealedMapLink<super::ByMapCatchAllKind> for F
where
- F: for<'a> FnMut(&'a MapValue, DefRegionKind) -> O,
+ F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
O: IntoMapResult,
{
}
@@ -853,7 +1226,11 @@ where
O: IntoMapResult,
{
#[inline]
- fn try_map(&mut self, value: &MapValue, _def_region_kind: DefRegionKind)
-> Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ _def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
value.cast::<T>().map(|typed| self(typed).into_map_result())
}
}
@@ -868,7 +1245,11 @@ where
O: IntoMapResult,
{
#[inline]
- fn try_map(&mut self, value: &MapValue, def_region_kind: DefRegionKind) ->
Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
value
.cast::<T>()
.map(|typed| self(typed, def_region_kind).into_map_result())
@@ -885,7 +1266,11 @@ where
O: IntoMapResult,
{
#[inline]
- fn try_map(&mut self, value: &MapValue, _def_region_kind: DefRegionKind)
-> Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ _def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
value
.as_node::<N>()
.map(|node| self(node).into_map_result())
@@ -902,7 +1287,11 @@ where
O: IntoMapResult,
{
#[inline]
- fn try_map(&mut self, value: &MapValue, def_region_kind: DefRegionKind) ->
Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
value
.as_node::<N>()
.map(|node| self(node, def_region_kind).into_map_result())
@@ -914,11 +1303,15 @@ pub enum ByMapCatchAll {}
impl<F, O> MapChainLink<ByMapCatchAll> for F
where
- F: for<'a> FnMut(&'a MapValue) -> O,
+ F: for<'a> FnMut(&'a StructuralView) -> O,
O: IntoMapResult,
{
#[inline]
- fn try_map(&mut self, value: &MapValue, _def_region_kind: DefRegionKind)
-> Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ _def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
Some(self(value).into_map_result())
}
}
@@ -928,11 +1321,15 @@ pub enum ByMapCatchAllKind {}
impl<F, O> MapChainLink<ByMapCatchAllKind> for F
where
- F: for<'a> FnMut(&'a MapValue, DefRegionKind) -> O,
+ F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
O: IntoMapResult,
{
#[inline]
- fn try_map(&mut self, value: &MapValue, def_region_kind: DefRegionKind) ->
Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
Some(self(value, def_region_kind).into_map_result())
}
}
@@ -945,7 +1342,11 @@ pub enum ByMapDispatchLink {}
impl<V: MapDispatch> MapChainLink<ByMapDispatchLink> for &mut V {
#[inline]
- fn try_map(&mut self, value: &MapValue, def_region_kind: DefRegionKind) ->
Option<MapResult> {
+ fn try_map(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Option<MapResult> {
self.dispatch_map(value, def_region_kind)
}
}
@@ -974,7 +1375,7 @@ where
#[inline]
fn dispatch_map(
&mut self,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<MapResult> {
self.link.try_map(value, def_region_kind)
@@ -996,7 +1397,7 @@ macro_rules! impl_map_chain_link {
#[inline]
fn try_map(
&mut self,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<MapResult> {
$(
@@ -1053,7 +1454,7 @@ impl_bare_map_link!(
impl<F, O> IntoMapper<ByMapCatchAll> for F
where
- F: for<'a> FnMut(&'a MapValue) -> O,
+ F: for<'a> FnMut(&'a StructuralView) -> O,
O: IntoMapResult,
{
type Mapper = MapChain<F, ByMapCatchAll>;
@@ -1066,7 +1467,7 @@ where
impl<F, O> IntoMapper<ByMapCatchAllKind> for F
where
- F: for<'a> FnMut(&'a MapValue, DefRegionKind) -> O,
+ F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
O: IntoMapResult,
{
type Mapper = MapChain<F, ByMapCatchAllKind>;
@@ -1081,7 +1482,7 @@ where
///
/// The engine issues it only when the current ownership path permits reuse.
pub struct InplaceValue<'a> {
- value: MapValue,
+ value: StructuralView,
_scope: PhantomData<&'a mut TVMFFIAny>,
}
@@ -1089,14 +1490,14 @@ impl<'a> InplaceValue<'a> {
#[inline]
fn from_raw(raw: &'a mut TVMFFIAny) -> Self {
Self {
- value: MapValue::from_raw(*raw),
+ value: StructuralView::from_raw(*raw),
_scope: PhantomData,
}
}
/// Borrow the value without its in-place capability.
#[inline]
- pub fn as_value(&self) -> &MapValue {
+ pub fn as_value(&self) -> &StructuralView {
&self.value
}
@@ -1111,7 +1512,7 @@ impl<'a> InplaceValue<'a> {
}
impl Deref for InplaceValue<'_> {
- type Target = MapValue;
+ type Target = StructuralView;
#[inline]
fn deref(&self) -> &Self::Target {
@@ -1129,13 +1530,13 @@ pub struct StructuralVarRemap {
impl StructuralVarRemap {
/// Look up an identity replacement previously stored for `var`.
- pub fn get(&self, var: &MapValue) -> Result<Option<Any>> {
+ pub fn get(&self, var: &StructuralView) -> Result<Option<Any>> {
let key = object_identity_key(var.raw())?;
Ok(self.entries.get(&key).map(|entry| entry.result.clone()))
}
/// Store a descent result or an [`Unchanged`] marker for `var`.
- pub fn set(&mut self, var: &MapValue, mutated_value: &Any) -> Result<()> {
+ pub fn set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()> {
let key = object_identity_key(var.raw())?;
self.entries.insert(
key,
@@ -1162,7 +1563,11 @@ pub trait StructuralMutator: Sized {
/// Dispatch one borrowed value without modifying its source storage.
///
/// The structural-mutation engine calls this hook for each value.
- fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind:
DefRegionKind) -> Result<Any>;
+ fn dispatch_mutate(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<Any>;
/// Dispatch one value for which the engine permits an in-place attempt.
///
@@ -1191,6 +1596,23 @@ pub trait StructuralMutator: Sized {
/// Re-enter this mutator for an owned value, permitting reuse only when
/// the converted value remains uniquely owned.
fn maybe_inplace_mutate<T>(&mut self, value: T, def_region_kind:
DefRegionKind) -> Result<Any>
+ where
+ T: Into<Any>,
+ {
+ self.maybe_inplace_mutate_with_mode(value, def_region_kind,
InplaceMode::Allow)
+ }
+
+ /// Re-enter for an owned value with an explicit region and in-place
permission.
+ ///
+ /// `Allow` checks uniqueness before dispatch; `Disallow` uses ordinary
+ /// mutation without checking uniqueness. An independently owned
replacement
+ /// or a later explicit owned entry can establish a new permission
boundary.
+ fn maybe_inplace_mutate_with_mode<T>(
+ &mut self,
+ value: T,
+ def_region_kind: DefRegionKind,
+ mode: InplaceMode,
+ ) -> Result<Any>
where
T: Into<Any>,
{
@@ -1199,13 +1621,17 @@ pub trait StructuralMutator: Sized {
self,
*value.as_raw_ffi_any(),
def_region_kind,
- Permit::MaybeInPlace,
+ mode.permit(),
)?;
Ok(if is_unchanged(&result) { value } else { result })
}
/// Apply default non-in-place mutation to `value`'s children.
- fn default_mutate(&mut self, value: &MapValue, def_region_kind:
DefRegionKind) -> Result<Any> {
+ 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()))
}
@@ -1233,13 +1659,25 @@ pub trait StructuralMutator: Sized {
&mut self,
value: InplaceValue<'_>,
def_region_kind: DefRegionKind,
+ ) -> Result<Any> {
+ self.default_maybe_inplace_mutate_with_mode(value, def_region_kind,
InplaceMode::Allow)
+ }
+
+ /// Apply default mutation with a capability and an explicit permission.
+ ///
+ /// `Disallow` overrides the capability and uses the copy path. `Allow`
+ /// rechecks uniqueness because the handler may have retained an owning
+ /// alias. Consuming the capability prevents a borrow from it surviving
this
+ /// call. Unlike the C++ default-descent API, safe Rust cannot simply trust
+ /// the earlier uniqueness check after arbitrary user code has run.
+ fn default_maybe_inplace_mutate_with_mode(
+ &mut self,
+ value: InplaceValue<'_>,
+ def_region_kind: DefRegionKind,
+ mode: InplaceMode,
) -> Result<Any> {
let raw = value.raw();
- let permit = if object_is_unique(raw) {
- Permit::MaybeInPlace
- } else {
- Permit::Copy
- };
+ let permit = mode.permit_if_unique(raw);
user_default_mutate(self, raw, def_region_kind, permit)
.and_then(|result| resolve_result(result, raw))
}
@@ -1257,7 +1695,7 @@ pub trait StructuralMutator: Sized {
/// Default non-in-place mutation with an unchanged-or-replacement result.
fn default_mutate_result(
&mut self,
- value: &MapValue,
+ value: &StructuralView,
kind: DefRegionKind,
) -> Result<UnchangedOr<Any>> {
user_default_mutate(self, value.raw(), kind, Permit::Copy)
@@ -1283,36 +1721,52 @@ pub trait StructuralMutator: Sized {
&mut self,
value: InplaceValue<'_>,
kind: DefRegionKind,
+ ) -> Result<UnchangedOr<Any>> {
+ self.default_maybe_inplace_mutate_with_mode_result(value, kind,
InplaceMode::Allow)
+ }
+
+ /// Default mutation with a capability and permission, preserving
unchanged.
+ ///
+ /// Uses the same ownership checks as
[`Self::default_maybe_inplace_mutate_with_mode`].
+ fn default_maybe_inplace_mutate_with_mode_result(
+ &mut self,
+ value: InplaceValue<'_>,
+ kind: DefRegionKind,
+ mode: InplaceMode,
) -> Result<UnchangedOr<Any>> {
let raw = value.raw();
- let permit = if object_is_unique(raw) {
- Permit::MaybeInPlace
- } else {
- Permit::Copy
- };
+ let permit = mode.permit_if_unique(raw);
user_default_mutate(self, raw, kind,
permit).and_then(UnchangedOr::from_carrier)
}
/// Look up a FreeVar or DAG-node substitution from the active mutation.
- fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+ fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
invocation_var_remap_get(self, var)
}
/// Store a FreeVar or DAG-node substitution for the active mutation.
- fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) ->
Result<()> {
+ fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()> {
invocation_var_remap_set(self, var, mutated_value)
}
}
impl<D: MutateDispatch> StructuralMutator for D {
#[inline(always)]
- 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> {
let mut mutator = Mutator {
- current: MapValue::from_raw(value.raw()),
def_region_kind,
+ inplace_mode: InplaceMode::Disallow,
_not_send_sync: PhantomData,
};
- match MutateDispatch::dispatch_mutate(self, value, &mut mutator) {
+ match MutateDispatch::dispatch_mutate_value(
+ self,
+ MutateValue::borrowed(value),
+ &mut mutator,
+ ) {
Some(result) => result,
None => user_default_mutate(self, value.raw(), def_region_kind,
Permit::Copy),
}
@@ -1325,11 +1779,15 @@ impl<D: MutateDispatch> StructuralMutator for D {
def_region_kind: DefRegionKind,
) -> Result<Any> {
let mut mutator = Mutator {
- current: MapValue::from_raw(value.raw()),
def_region_kind,
+ inplace_mode: InplaceMode::Allow,
_not_send_sync: PhantomData,
};
- match MutateDispatch::dispatch_mutate(self, value.as_value(), &mut
mutator) {
+ match MutateDispatch::dispatch_mutate_value(
+ self,
+ MutateValue::new(value.as_value(), InplaceMode::Allow),
+ &mut mutator,
+ ) {
Some(result) => result,
None => {
let raw = value.raw();
@@ -1350,8 +1808,9 @@ trait MutateCallbackStrategy<State, Link, Marker> {
fn try_mutate<Driver>(
driver: &mut Driver,
callback_ptr: *const Link,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
+ inplace_mode: InplaceMode,
) -> Option<MutateResult>
where
Driver: MutateContextDriver<State>;
@@ -1365,22 +1824,28 @@ where
fn try_mutate<Driver>(
driver: &mut Driver,
callback_ptr: *const Link,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
+ inplace_mode: InplaceMode,
) -> Option<MutateResult>
where
Driver: MutateContextDriver<State>,
{
let mut mutator = MutateContext::<State, dyn
MutateContextDriver<State>> {
driver,
- current: MapValue::from_raw(value.raw()),
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(value, &mut mutator) }
+ unsafe {
+ (&*callback_ptr).try_mutate(
+ &mut Some(MutateValue::new(value, inplace_mode)),
+ &mut mutator,
+ )
+ }
}
}
@@ -1388,8 +1853,9 @@ where
fn try_mutate_callbacks<State, Link, Marker, Driver>(
driver: &mut Driver,
callback_ptr: *const Link,
- value: &MapValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
+ inplace_mode: InplaceMode,
) -> Option<MutateResult>
where
Link: MutateChainLink<State, Marker>,
@@ -1401,6 +1867,7 @@ where
callback_ptr,
value,
def_region_kind,
+ inplace_mode,
)
}
@@ -1410,13 +1877,18 @@ where
Link::Strategy: MutateCallbackStrategy<State, Link, Marker>,
{
#[inline(always)]
- 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> {
let callback_ptr = Rc::as_ptr(&self.callbacks);
match try_mutate_callbacks::<State, Link, Marker, _>(
self,
callback_ptr,
value,
def_region_kind,
+ InplaceMode::Disallow,
) {
Some(result) => result,
None => user_default_mutate(self, value.raw(), def_region_kind,
Permit::Copy),
@@ -1435,6 +1907,7 @@ where
callback_ptr,
value.as_value(),
def_region_kind,
+ InplaceMode::Allow,
) {
Some(result) => result,
None => self
@@ -1450,13 +1923,18 @@ where
Link::Strategy: MutateCallbackStrategy<(), Link, Marker>,
{
#[inline(always)]
- 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> {
let callback_ptr = std::ptr::from_ref(self.callbacks);
match try_mutate_callbacks::<(), Link, Marker, _>(
self,
callback_ptr,
value,
def_region_kind,
+ InplaceMode::Disallow,
) {
Some(result) => result,
None => user_default_mutate(self, value.raw(), def_region_kind,
Permit::Copy),
@@ -1475,6 +1953,7 @@ where
callback_ptr,
value.as_value(),
def_region_kind,
+ InplaceMode::Allow,
) {
Some(result) => result,
None => self
@@ -1499,38 +1978,43 @@ where
}
#[inline(always)]
- fn mutate_raw(
- &mut self,
- raw: TVMFFIAny,
- def_region_kind: DefRegionKind,
- permit: Permit,
- ) -> Result<Any> {
- dispatch_user_raw(self, raw, def_region_kind, permit)
+ fn mutate_borrowed(&mut self, value: AnyView<'_>, kind: DefRegionKind) ->
Result<Any> {
+ dispatch_user_raw(self, *value.as_raw_ffi_any(), kind, Permit::Copy)
}
#[inline(always)]
- fn default_mutate_raw(
+ fn mutate_owned(&mut self, value: Any, kind: DefRegionKind, mode:
InplaceMode) -> Result<Any> {
+ StructuralMutator::maybe_inplace_mutate_with_mode(self, value, kind,
mode)
+ }
+
+ #[inline(always)]
+ fn default_mutate_borrowed(&mut self, value: AnyView<'_>, kind:
DefRegionKind) -> Result<Any> {
+ default_mutate_driver(self, *value.as_raw_ffi_any(), kind,
Permit::Copy)
+ }
+
+ fn default_mutate_value(
&mut self,
- raw: TVMFFIAny,
- def_region_kind: DefRegionKind,
+ value: MutateValue<'_>,
+ kind: DefRegionKind,
+ mode: InplaceMode,
) -> Result<Any> {
- default_mutate_driver(self, raw, def_region_kind, Permit::Copy)
+ let permit = value.permit(mode);
+ default_mutate_driver(self, value.value.raw(), kind, permit)
}
#[inline(always)]
- fn var_remap_get_raw(&mut self, raw: TVMFFIAny) -> Result<Option<Any>> {
- <Self as StructuralMutator>::var_remap_get(self,
&MapValue::from_raw(raw))
+ fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
+ <Self as StructuralMutator>::var_remap_get(self, var)
}
#[inline(always)]
- fn var_remap_set_raw(&mut self, raw: TVMFFIAny, mutated_value: &Any) ->
Result<()> {
- <Self as StructuralMutator>::var_remap_set(self,
&MapValue::from_raw(raw), mutated_value)
+ fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) ->
Result<()> {
+ <Self as StructuralMutator>::var_remap_set(self, var, mutated_value)
}
}
-#[doc(hidden)]
#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum Permit {
+enum Permit {
Copy,
MaybeInPlace,
}
@@ -1562,7 +2046,7 @@ impl<D: MapDispatch> NativeMapper<D> {
// excluded because converting those borrowed special values into an
// Any performs normalization rather than a bitwise copy.
if is_plain_inline(raw.type_index) {
- let value = MapValue::from_raw(raw);
+ let value = StructuralView::from_raw(raw);
return match self.dispatch.dispatch_map(&value, def_region_kind) {
Some(result) => {
let mapped = result?;
@@ -1599,7 +2083,7 @@ impl<D: MapDispatch> NativeMapper<D> {
) -> Result<Any> {
match self.order {
WalkOrder::PreOrder => {
- let value = MapValue::from_raw(raw);
+ let value = StructuralView::from_raw(raw);
let Some(callback_result) = self.dispatch.dispatch_map(&value,
def_region_kind)
else {
return self.default_map_current_raw(raw, def_region_kind,
permit);
@@ -1628,7 +2112,7 @@ impl<D: MapDispatch> NativeMapper<D> {
} else {
*mapped.as_raw_ffi_any()
};
- let value = MapValue::from_raw(mapped_raw);
+ let value = StructuralView::from_raw(mapped_raw);
match self.dispatch.dispatch_map(&value, def_region_kind) {
Some(result) => result,
None => Ok(mapped),
@@ -2067,7 +2551,10 @@ fn active_mutator() -> Result<StructuralMutatorHandle> {
})
}
-fn invocation_var_remap_get<U: Sized>(mutator: &mut U, var: &MapValue) ->
Result<Option<Any>> {
+fn invocation_var_remap_get<U: Sized>(
+ mutator: &mut U,
+ var: &StructuralView,
+) -> Result<Option<Any>> {
let active = active_mutator()?;
let context = std::ptr::from_mut(mutator).cast::<c_void>();
unsafe {
@@ -2082,7 +2569,7 @@ fn invocation_var_remap_get<U: Sized>(mutator: &mut U,
var: &MapValue) -> Result
fn invocation_var_remap_set<U: Sized>(
mutator: &mut U,
- var: &MapValue,
+ var: &StructuralView,
mutated_value: &Any,
) -> Result<()> {
let active = active_mutator()?;
@@ -2185,11 +2672,11 @@ impl<D: MapDispatch> MutationDriver for NativeMapper<D>
{
}
fn var_remap_get_raw(&mut self, raw: TVMFFIAny) -> Result<Option<Any>> {
- self.remap.get(&MapValue::from_raw(raw))
+ self.remap.get(&StructuralView::from_raw(raw))
}
fn var_remap_set_raw(&mut self, raw: TVMFFIAny, replacement: &Any) ->
Result<()> {
- self.remap.set(&MapValue::from_raw(raw), replacement)
+ self.remap.set(&StructuralView::from_raw(raw), replacement)
}
}
@@ -2204,11 +2691,11 @@ impl<U: StructuralMutator> MutationDriver for U {
}
fn var_remap_get_raw(&mut self, raw: TVMFFIAny) -> Result<Option<Any>> {
- self.var_remap_get(&MapValue::from_raw(raw))
+ self.var_remap_get(&StructuralView::from_raw(raw))
}
fn var_remap_set_raw(&mut self, raw: TVMFFIAny, replacement: &Any) ->
Result<()> {
- self.var_remap_set(&MapValue::from_raw(raw), replacement)
+ self.var_remap_set(&StructuralView::from_raw(raw), replacement)
}
}
@@ -2480,7 +2967,7 @@ fn dispatch_user_raw<U: StructuralMutator>(
mutator
.dispatch_maybe_inplace_mutate(InplaceValue::from_raw(&mut
scoped_raw), def_region_kind)
} else {
- mutator.dispatch_mutate(&MapValue::from_raw(raw), def_region_kind)
+ mutator.dispatch_mutate(&StructuralView::from_raw(raw),
def_region_kind)
};
result.map_err(|error| with_value_context(error, raw))
}
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index 0fc337ad..9031de5f 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -35,7 +35,7 @@
//! opt in with a trailing [`DefRegionKind`] argument, and a visitor receives
//! and forwards it when descending.
//!
-//! Underneath both, [`VisitValue`] provides borrowed matching for typed Rust
+//! Underneath both, [`StructuralView`] provides borrowed matching for typed
Rust
//! dispatch. Rust supplies a temporary `ffi.StructuralVisitor` ABI object so
//! every type's registered `__s_visit__` hook can enumerate its children and
//! call back into the active Rust visitor. Types without a hook fall back to
@@ -204,12 +204,10 @@ impl IntoVisitResult for Result<Option<VisitInterrupt>> {
#[doc(hidden)]
pub type WalkCallbackResult = Result<WalkResult>;
-/// A borrowed view of a raw tvm-ffi value passed to structural-visit
callbacks.
-///
-/// Generated visitors match this value without taking ownership: borrowed
-/// object-node handlers use [`VisitValue::as_node`], while POD or object-ref
-/// value handlers use [`VisitValue::cast`].
-pub use super::structural_common::StructuralValue as VisitValue;
+pub use super::StructuralView;
+
+/// Compatibility name for [`StructuralView`].
+pub use super::StructuralView as VisitValue;
enum NativeHalt {
Interrupt(Any),
@@ -233,7 +231,7 @@ pub use policy::{DefaultVisitPolicy, VisitPolicy,
WalkWithPolicy};
/// reborrow the visitor, so mutable state cannot remain borrowed across them.
pub struct VisitContext<'a, State> {
driver: &'a mut dyn VisitContextDriver<State>,
- current: VisitValue,
+ current: StructuralView,
def_region_kind: DefRegionKind,
_not_send_sync: PhantomData<Rc<()>>,
}
@@ -265,7 +263,7 @@ impl<State> VisitContext<'_, State> {
}
/// Complete borrowed value active at this callback.
- pub fn current(&self) -> &VisitValue {
+ pub fn current(&self) -> &StructuralView {
&self.current
}
@@ -352,7 +350,7 @@ impl<State> VisitContext<'_, State> {
/// Use [`VisitCallbacks`] when the chain needs mutable state.
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a supported `structural_visit` visitor",
- note = "accepted visitors: `&mut V` where `V: StructuralVisitor`; an `Fn`
callback over an FFI value type `T`, `&N` of an object node type, or
`&VisitValue`, followed by `&mut VisitContext<'_, ()>`; or a tuple of up to 12
such callbacks (tuples may nest)",
+ note = "accepted visitors: `&mut V` where `V: StructuralVisitor`; an `Fn`
callback over an FFI value type `T`, `&N` of an object node type, or
`&StructuralView`, followed by `&mut VisitContext<'_, ()>`; or a tuple of up to
12 such callbacks (tuples may nest)",
note = "callback arguments need explicit type annotations; use
`VisitCallbacks::new(state, callbacks)` for ordinary mutable callback state"
)]
pub trait IntoVisitor<Marker> {
@@ -375,13 +373,13 @@ pub trait VisitChainLink<State, Marker>:
visit_sealed::SealedLink<State, Marker>
#[doc(hidden)]
fn try_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Option<Result<Option<VisitInterrupt>>>;
}
mod visit_sealed {
- use super::{IntoVisitResult, ObjectCore, VisitContext, VisitValue};
+ use super::{IntoVisitResult, ObjectCore, StructuralView, VisitContext};
pub trait SealedLink<State, Marker> {}
@@ -405,7 +403,7 @@ mod visit_sealed {
impl<F, State, O> SealedLink<State, super::ByVisitCatchAllLink> for F
where
F: for<'value, 'visitor, 'driver> Fn(
- &'value VisitValue,
+ &'value StructuralView,
&'visitor mut VisitContext<'driver, State>,
) -> O,
O: IntoVisitResult,
@@ -424,7 +422,7 @@ where
{
fn try_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Option<Result<Option<VisitInterrupt>>> {
value
@@ -447,7 +445,7 @@ where
{
fn try_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Option<Result<Option<VisitInterrupt>>> {
value
@@ -462,14 +460,14 @@ pub enum ByVisitCatchAllLink {}
impl<F, State, O> VisitChainLink<State, ByVisitCatchAllLink> for F
where
F: for<'value, 'visitor, 'driver> Fn(
- &'value VisitValue,
+ &'value StructuralView,
&'visitor mut VisitContext<'driver, State>,
) -> O,
O: IntoVisitResult,
{
fn try_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Option<Result<Option<VisitInterrupt>>> {
Some(self(value, visitor).into_visit_result())
@@ -495,7 +493,7 @@ macro_rules! impl_visit_chain_link {
{
fn try_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Option<Result<Option<VisitInterrupt>>> {
$(
@@ -629,7 +627,7 @@ pub use super::dispatch::{ByWalkDispatch, DispatchWalker,
WalkDispatch};
/// tuple. `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
`&VisitValue`, \
+ 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)",
@@ -651,10 +649,14 @@ pub struct ClosureWalker<F> {
impl<F, O> NativeVisit for ClosureWalker<F>
where
- F: FnMut(&VisitValue) -> O,
+ F: FnMut(&StructuralView) -> O,
O: IntoWalkResult,
{
- fn visit(&mut self, value: &VisitValue, _def_region_kind: DefRegionKind)
-> Result<WalkResult> {
+ fn visit(
+ &mut self,
+ value: &StructuralView,
+ _def_region_kind: DefRegionKind,
+ ) -> Result<WalkResult> {
(self.callback)(value).into_walk_result()
}
}
@@ -664,7 +666,7 @@ pub enum ByValueClosure {}
impl<F, O> IntoWalker<ByValueClosure> for F
where
- F: FnMut(&VisitValue) -> O,
+ F: FnMut(&StructuralView) -> O,
O: IntoWalkResult,
{
type Walker = ClosureWalker<F>;
@@ -681,10 +683,14 @@ pub struct ClosureKindWalker<F> {
impl<F, O> NativeVisit for ClosureKindWalker<F>
where
- F: FnMut(&VisitValue, DefRegionKind) -> O,
+ F: FnMut(&StructuralView, DefRegionKind) -> O,
O: IntoWalkResult,
{
- fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) ->
Result<WalkResult> {
+ fn visit(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<WalkResult> {
(self.callback)(value, def_region_kind).into_walk_result()
}
}
@@ -694,7 +700,7 @@ pub enum ByValueKindClosure {}
impl<F, O> IntoWalker<ByValueKindClosure> for F
where
- F: FnMut(&VisitValue, DefRegionKind) -> O,
+ F: FnMut(&StructuralView, DefRegionKind) -> O,
O: IntoWalkResult,
{
type Walker = ClosureKindWalker<F>;
@@ -705,7 +711,7 @@ where
/// One link in a first-match [`structural_walk`] callback chain.
///
-/// Supported links are typed values, borrowed object nodes, `&VisitValue`,
+/// Supported links are typed values, borrowed object nodes, `&StructuralView`,
/// and mutable [`WalkDispatch`] implementations, optionally followed by
/// [`DefRegionKind`]. Tuples hold up to 12 links and may be nested.
pub trait WalkChainLink<Marker>: sealed::SealedLink<Marker> {
@@ -714,13 +720,13 @@ pub trait WalkChainLink<Marker>:
sealed::SealedLink<Marker> {
#[doc(hidden)]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult>;
}
mod sealed {
- use super::{DefRegionKind, IntoWalkResult, ObjectCore, VisitValue,
WalkDispatch};
+ use super::{DefRegionKind, IntoWalkResult, ObjectCore, StructuralView,
WalkDispatch};
pub trait SealedLink<Marker> {}
@@ -750,13 +756,13 @@ mod sealed {
}
impl<F, O> SealedLink<super::ByCatchAllLink> for F
where
- F: for<'a> FnMut(&'a VisitValue) -> O,
+ F: for<'a> FnMut(&'a StructuralView) -> O,
O: IntoWalkResult,
{
}
impl<F, O> SealedLink<super::ByCatchAllKindLink> for F
where
- F: for<'a> FnMut(&'a VisitValue, DefRegionKind) -> O,
+ F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
O: IntoWalkResult,
{
}
@@ -775,7 +781,7 @@ where
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
_def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
value
@@ -796,7 +802,7 @@ where
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
value
@@ -817,7 +823,7 @@ where
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
_def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
value
@@ -838,7 +844,7 @@ where
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
value
@@ -852,13 +858,13 @@ pub enum ByCatchAllLink {}
impl<F, O> WalkChainLink<ByCatchAllLink> for F
where
- F: for<'a> FnMut(&'a VisitValue) -> O,
+ F: for<'a> FnMut(&'a StructuralView) -> O,
O: IntoWalkResult,
{
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
_def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
Some(self(value).into_walk_result())
@@ -870,13 +876,13 @@ pub enum ByCatchAllKindLink {}
impl<F, O> WalkChainLink<ByCatchAllKindLink> for F
where
- F: for<'a> FnMut(&'a VisitValue, DefRegionKind) -> O,
+ F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
O: IntoWalkResult,
{
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
Some(self(value, def_region_kind).into_walk_result())
@@ -893,7 +899,7 @@ impl<V: WalkDispatch> WalkChainLink<ByWalkDispatchLink> for
&mut V {
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
self.dispatch_walk(value, def_region_kind)
@@ -922,7 +928,11 @@ where
Link: WalkChainLink<Marker>,
{
#[inline]
- fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) ->
Result<WalkResult> {
+ fn visit(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<WalkResult> {
self.link
.try_call(value, def_region_kind)
.unwrap_or(Ok(WalkResult::Advance))
@@ -944,7 +954,7 @@ macro_rules! impl_chain_link {
#[inline]
fn try_call(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Option<WalkCallbackResult> {
$(
@@ -1004,7 +1014,7 @@ pub trait StructuralVisitor: Sized {
/// Visit one value under the definition-region state active at it.
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>>;
@@ -1033,7 +1043,7 @@ pub trait StructuralVisitor: Sized {
#[inline]
fn default_visit_children(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
default_user_visit_children(self, value, def_region_kind)
@@ -1042,7 +1052,7 @@ pub trait StructuralVisitor: Sized {
fn default_user_visit_children<V: StructuralVisitor>(
visitor: &mut V,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let raw = value.raw();
@@ -1056,7 +1066,7 @@ fn default_user_visit_children<V: StructuralVisitor>(
fn try_visit_callbacks<State, Link, Marker>(
driver: &mut impl VisitContextDriver<State>,
callback_ptr: *const Link,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>>
where
@@ -1064,7 +1074,7 @@ where
{
let mut visitor = VisitContext {
driver,
- current: VisitValue::from_raw(value.raw()),
+ current: StructuralView::from_raw(value.raw()),
def_region_kind,
_not_send_sync: PhantomData,
};
@@ -1083,7 +1093,7 @@ where
{
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let callback_ptr = Rc::as_ptr(&self.callbacks);
@@ -1092,7 +1102,7 @@ where
fn default_visit_children(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let Some(policy) = self.policy.as_ref().map(Rc::clone) else {
@@ -1113,7 +1123,7 @@ where
{
fn visit(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let callback_ptr = std::ptr::from_ref(self.callbacks);
@@ -1155,7 +1165,7 @@ where
) -> Result<Option<VisitInterrupt>> {
<Self as StructuralVisitor>::default_visit_children(
self,
- &VisitValue::from_raw(raw),
+ &StructuralView::from_raw(raw),
def_region_kind,
)
}
@@ -1166,11 +1176,15 @@ where
pub trait NativeVisit: Sized {
const CUSTOM_DESCENT: bool = false;
- fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) ->
Result<WalkResult>;
+ fn visit(
+ &mut self,
+ value: &StructuralView,
+ def_region_kind: DefRegionKind,
+ ) -> Result<WalkResult>;
fn default_visit_children<const PRE_ORDER: bool>(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
default_walk_children::<Self, PRE_ORDER>(self, value, def_region_kind)
@@ -1179,7 +1193,7 @@ pub trait NativeVisit: Sized {
fn default_walk_children<V: NativeVisit, const PRE_ORDER: bool>(
visitor: &mut V,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let context = std::ptr::from_mut(&mut *visitor).cast::<c_void>();
@@ -1218,7 +1232,7 @@ impl<V: StructuralVisitor> ChildVisit for
UserChildren<'_, V> {
}
match self
.visitor
- .visit(&VisitValue::from_raw(child), def_region_kind)
+ .visit(&StructuralView::from_raw(child), def_region_kind)
{
Ok(None) => Ok(()),
Ok(Some(interrupt)) => Err(NativeHalt::Interrupt(interrupt.value)),
@@ -1238,7 +1252,7 @@ fn visit_raw<V: NativeVisit, const PRE_ORDER: bool>(
return Ok(());
}
- let visit_value = VisitValue::from_raw(value);
+ let visit_value = StructuralView::from_raw(value);
if PRE_ORDER {
match visitor.visit(&visit_value, def_region_kind) {
Ok(WalkResult::Advance) => {}
@@ -1679,7 +1693,7 @@ unsafe fn runtime_walk<V: NativeVisit, const PRE_ORDER:
bool>(
if !V::CUSTOM_DESCENT && raw.type_index <
TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
let visitor = &mut *context.cast::<V>();
if PRE_ORDER {
- match visitor.visit(&VisitValue::from_raw(raw), def_region_kind) {
+ match visitor.visit(&StructuralView::from_raw(raw),
def_region_kind) {
Ok(WalkResult::Advance) => {}
Ok(WalkResult::Skip) => return Ok(()),
Ok(WalkResult::Interrupt) => return
Err(NativeHalt::Interrupt(Any::new())),
@@ -1698,7 +1712,7 @@ unsafe fn runtime_walk<V: NativeVisit, const PRE_ORDER:
bool>(
// Post-order inline values have no children unless their type
// registered a visit hook. Handle the common case directly here.
if !has_registered_visit_hook(raw.type_index) {
- return match visitor.visit(&VisitValue::from_raw(raw),
def_region_kind) {
+ return match visitor.visit(&StructuralView::from_raw(raw),
def_region_kind) {
Ok(WalkResult::Advance | WalkResult::Skip) => Ok(()),
Ok(WalkResult::Interrupt) =>
Err(NativeHalt::Interrupt(Any::new())),
Ok(WalkResult::InterruptWith(payload)) =>
Err(NativeHalt::Interrupt(payload)),
@@ -1718,7 +1732,7 @@ unsafe fn runtime_user_visit<V: StructuralVisitor>(
if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
return Ok(());
}
- match (&mut *context.cast::<V>()).visit(&VisitValue::from_raw(raw),
def_region_kind) {
+ match (&mut *context.cast::<V>()).visit(&StructuralView::from_raw(raw),
def_region_kind) {
Ok(None) => Ok(()),
Ok(Some(interrupt)) => Err(NativeHalt::Interrupt(interrupt.value)),
Err(error) => Err(NativeHalt::Error(error)),
@@ -1940,7 +1954,7 @@ where
///
/// `walker` is anything implementing [`IntoWalker`]: a `&mut` reference to a
/// stateful [`WalkDispatch`] walker (`#[dispatch(walk)]`), a bare closure
-/// in any [`WalkChainLink`] shape (catch-all `&VisitValue`, typed, or node,
+/// 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,
diff --git a/rust/tvm-ffi/src/extra/structural_visit/policy.rs
b/rust/tvm-ffi/src/extra/structural_visit/policy.rs
index e2372ee2..9b6b3d1c 100644
--- a/rust/tvm-ffi/src/extra/structural_visit/policy.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit/policy.rs
@@ -37,7 +37,7 @@ pub trait VisitPolicy<State> {
/// or when a matched callback requests default descent.
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Result<Option<VisitInterrupt>>;
}
@@ -48,7 +48,7 @@ pub struct DefaultVisitPolicy;
impl<State> VisitPolicy<State> for DefaultVisitPolicy {
fn default_visit(
&self,
- _value: &VisitValue,
+ _value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Result<Option<VisitInterrupt>> {
visitor.visit_children()
@@ -60,7 +60,7 @@ impl<State, Outer: VisitPolicy<State>, Inner:
VisitPolicy<State>> VisitPolicy<St
{
fn default_visit(
&self,
- value: &VisitValue,
+ value: &StructuralView,
visitor: &mut VisitContext<'_, State>,
) -> Result<Option<VisitInterrupt>> {
let kind = visitor.def_region_kind();
@@ -79,14 +79,14 @@ impl<State, Outer: VisitPolicy<State>, Inner:
VisitPolicy<State>> VisitPolicy<St
pub(super) fn visit_with_policy<State>(
driver: &mut dyn VisitContextDriver<State>,
policy: &impl VisitPolicy<State>,
- value: &VisitValue,
+ value: &StructuralView,
def_region_kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
policy.default_visit(
value,
&mut VisitContext {
driver,
- current: VisitValue::from_raw(value.raw()),
+ current: StructuralView::from_raw(value.raw()),
def_region_kind,
_not_send_sync: PhantomData,
},
@@ -115,7 +115,12 @@ impl<State, Policy: VisitPolicy<State>>
VisitContextDriver<State>
raw: TVMFFIAny,
kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
- visit_with_policy(self.driver, self.policy,
&VisitValue::from_raw(raw), kind)
+ visit_with_policy(
+ self.driver,
+ self.policy,
+ &StructuralView::from_raw(raw),
+ kind,
+ )
}
}
@@ -141,7 +146,7 @@ impl<State, V: StructuralVisitor +
VisitCallbackState<State>> VisitContextDriver
kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
// Bypass the current policy; children still re-enter the complete
visitor.
- default_user_visit_children(self.visitor, &VisitValue::from_raw(raw),
kind)
+ default_user_visit_children(self.visitor,
&StructuralView::from_raw(raw), kind)
}
}
@@ -213,7 +218,7 @@ impl<Walker: WalkDispatch, Policy: VisitPolicy<Walker>>
NativeVisit
{
const CUSTOM_DESCENT: bool = true;
- fn visit(&mut self, value: &VisitValue, kind: DefRegionKind) ->
Result<WalkResult> {
+ fn visit(&mut self, value: &StructuralView, kind: DefRegionKind) ->
Result<WalkResult> {
self.walker
.dispatch_walk(value, kind)
.unwrap_or(Ok(WalkResult::Advance))
@@ -221,7 +226,7 @@ impl<Walker: WalkDispatch, Policy: VisitPolicy<Walker>>
NativeVisit
fn default_visit_children<const PRE_ORDER: bool>(
&mut self,
- value: &VisitValue,
+ value: &StructuralView,
kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
let policy = Rc::clone(&self.policy);
@@ -267,6 +272,6 @@ impl<Walker: WalkDispatch, Policy: VisitPolicy<Walker>,
const PRE_ORDER: bool>
raw: TVMFFIAny,
kind: DefRegionKind,
) -> Result<Option<VisitInterrupt>> {
- default_walk_children::<_, PRE_ORDER>(self.visitor,
&VisitValue::from_raw(raw), kind)
+ default_walk_children::<_, PRE_ORDER>(self.visitor,
&StructuralView::from_raw(raw), kind)
}
}
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index dc44cedc..4bb5672c 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -49,9 +49,9 @@ pub use crate::error::{
};
pub use crate::extra::module::Module;
pub use crate::extra::structural_mutate::{
- structural_map, structural_mutate, CallbackMutator, InplaceValue,
IntoMapResult, IntoMapper,
- IntoMutator, MapChainLink, MapDispatch, MapValue, MutateCallbacks,
MutateChainLink,
- MutateContext, MutateDispatch, Mutator, StructuralMutator,
StructuralVarRemap,
+ structural_map, structural_mutate, CallbackMutator, InplaceMode,
InplaceValue, IntoMapResult,
+ IntoMapper, IntoMutator, MapChainLink, MapDispatch, MapValue,
MutateCallbacks, MutateChainLink,
+ MutateContext, MutateDispatch, MutateValue, Mutator, StructuralMutator,
StructuralVarRemap,
};
pub use crate::extra::structural_visit::{
structural_visit, structural_walk, DefRegionKind, DefaultVisitPolicy,
IntoVisitor,
@@ -60,6 +60,7 @@ pub use crate::extra::structural_visit::{
WalkWithPolicy,
};
pub use crate::extra::unchanged::{Unchanged, UnchangedOr};
+pub use crate::extra::StructuralView;
pub use crate::function::Function;
pub use crate::object::ObjectRefCast;
pub use crate::object::{
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index 14670061..b7647603 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -23,10 +23,10 @@ use tvm_ffi::function::FunctionObj;
use tvm_ffi::object::ObjectRef;
use tvm_ffi::{
dispatch, structural_map, structural_mutate, Any, AnyView, Array,
CallbackMutator,
- DefRegionKind, Error, FieldGetter, Function, InplaceValue, Map,
MapDispatch, MapValue,
- MutateCallbacks, Mutator, Object, ObjectArc, ObjectRefCore, Result, String
as FfiString,
- StructuralMutator, StructuralVarRemap, TypeIndex, Unchanged, UnchangedOr,
WalkOrder,
- RUNTIME_ERROR,
+ DefRegionKind, Error, FieldGetter, Function, InplaceMode, InplaceValue,
Map, MapDispatch,
+ MapValue, MutateCallbacks, MutateValue, Mutator, Object, ObjectArc,
ObjectRefCore, Result,
+ String as FfiString, StructuralMutator, StructuralVarRemap, TypeIndex,
Unchanged, UnchangedOr,
+ WalkOrder, RUNTIME_ERROR,
};
struct IncrementIntegers;
@@ -329,6 +329,336 @@ fn
user_mutator_recursive_entries_reenter_the_same_mutator() {
assert_eq!(mutated.get(0).unwrap(), 2);
}
+#[test]
+fn default_mutation_mode_preserves_ownership_and_unchanged_results() {
+ struct Controlled {
+ mode: InplaceMode,
+ increment: bool,
+ }
+ impl StructuralMutator for Controlled {
+ fn dispatch_mutate(&mut self, value: &MapValue, _: DefRegionKind) ->
Result<Any> {
+ let integer = value.cast::<i64>().unwrap();
+ Ok(if self.increment {
+ Any::from(integer + 1)
+ } else {
+ Unchanged.into()
+ })
+ }
+ fn dispatch_maybe_inplace_mutate(
+ &mut self,
+ value: InplaceValue<'_>,
+ kind: DefRegionKind,
+ ) -> Result<Any> {
+ if self.increment {
+ self.default_maybe_inplace_mutate_with_mode(value, kind,
self.mode)
+ } else {
+ let result =
+ self.default_maybe_inplace_mutate_with_mode_result(value,
kind, self.mode)?;
+ assert!(result.is_unchanged());
+ Ok(result.into())
+ }
+ }
+ }
+ for mode in [InplaceMode::Disallow, InplaceMode::Allow] {
+ for increment in [false, true] {
+ let root = Array::new(vec![1_i64]);
+ let pointer = array_pointer(&root);
+ let result = structural_mutate(root, &mut Controlled { mode,
increment })
+ .and_then(Array::<i64>::try_from)
+ .unwrap();
+ assert_eq!(
+ array_pointer(&result) == pointer,
+ mode == InplaceMode::Allow || !increment
+ );
+ assert_eq!(result.get(0).unwrap(), if increment { 2 } else { 1 });
+ }
+ }
+}
+
+#[test]
+fn owned_entry_mode_is_forwarded_by_generated_and_closure_callbacks() {
+ struct Entry {
+ mode: InplaceMode,
+ pointer: usize,
+ }
+ #[dispatch(mutate)]
+ impl Entry {
+ fn mutate_bool(&mut self, _: bool, ctx: &mut Mutator) -> Result<Any> {
+ let child = Array::new(vec![1_i64]);
+ self.pointer = array_pointer(&child) as usize;
+ ctx.maybe_inplace_mutate_with_mode(self, child,
DefRegionKind::Pattern, self.mode)
+ }
+ fn mutate_integer(&mut self, value: i64, ctx: &mut Mutator) -> i64 {
+ assert_eq!(ctx.def_region_kind(), DefRegionKind::Pattern);
+ value + 1
+ }
+ }
+ assert_eq!(InplaceMode::default(), InplaceMode::Disallow);
+ for mode in [InplaceMode::Disallow, InplaceMode::Allow] {
+ let mut entry = Entry { mode, pointer: 0 };
+ let result = structural_mutate(true, &mut entry)
+ .and_then(Array::<i64>::try_from)
+ .unwrap();
+ assert_eq!(
+ array_pointer(&result) as usize == entry.pointer,
+ mode == InplaceMode::Allow
+ );
+ assert_eq!(result.get(0).unwrap(), 2);
+
+ let pointer = Cell::new(0usize);
+ let result = structural_mutate(
+ true,
+ (
+ |_: bool, ctx: &mut CallbackMutator| {
+ 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| {
+ assert_eq!(ctx.def_region_kind(), DefRegionKind::Pattern);
+ value + 1
+ },
+ ),
+ )
+ .and_then(Array::<i64>::try_from)
+ .unwrap();
+ assert_eq!(
+ array_pointer(&result) as usize == pointer.get(),
+ mode == InplaceMode::Allow
+ );
+ assert_eq!(result.get(0).unwrap(), 2);
+ }
+}
+
+#[test]
+fn consuming_callbacks_forward_permissions_without_temporary_owners() {
+ struct Forward {
+ requested: InplaceMode,
+ retain: bool,
+ alias: Option<Any>,
+ modes: Vec<InplaceMode>,
+ }
+ impl Forward {
+ fn observe(&mut self, value: &MutateValue<'_, Array<Any>>) {
+ self.modes.push(value.inplace_mode());
+ let node = value
+ .as_node::<tvm_ffi::collections::array::ArrayObj>()
+ .unwrap();
+ assert_eq!(node.size, 1);
+ // A temporary typed handle must not permanently revoke permission.
+ let temporary = value.cast::<Array<Any>>().unwrap();
+ if self.retain && self.alias.is_none() {
+ self.alias = Some(temporary.clone().into());
+ }
+ drop(temporary);
+ }
+ }
+ #[dispatch(mutate)]
+ impl Forward {
+ fn mutate_string(&mut self, _: MutateValue<'_, FfiString>) -> Any {
+ panic!("typed miss must preserve the capability for the next
handler")
+ }
+ fn mutate_array(
+ &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());
+ 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());
+ value + 1
+ }
+ }
+ use InplaceMode::{Allow, Disallow};
+ for (requested, shared, retain) in [
+ (Allow, false, false),
+ (Disallow, false, false),
+ (Allow, true, false),
+ (Allow, false, true),
+ ] {
+ for generated in [true, false] {
+ let child = Array::new(vec![1_i64]);
+ let child_ptr = array_pointer(&child);
+ let root = Array::new(vec![child]);
+ let root_ptr = array_pointer(&root);
+ let original = shared.then(|| root.clone());
+ let mut state = Forward {
+ requested,
+ retain,
+ alias: None,
+ modes: vec![],
+ };
+ let result = if generated {
+ structural_mutate(root, &mut state).unwrap()
+ } else {
+ let miss = |_: MutateValue<'_, FfiString>,
+ _: &mut CallbackMutator<Forward>|
+ -> Any { panic!("typed miss must continue") };
+ let increment = |value: i64, _: &mut CallbackMutator<Forward>|
value + 1;
+ let descend = |value: MutateValue<'_, Array<Any>>,
+ ctx: &mut CallbackMutator<Forward>| {
+ assert_eq!(value.inplace_mode(), ctx.inplace_mode());
+ ctx.state_mut().observe(&value);
+ let requested = ctx.state().requested;
+ ctx.default_mutate_with_mode(value, requested)
+ };
+ let mut callbacks = MutateCallbacks::new(state, (miss,
(increment, descend)));
+ let result = structural_mutate(root, &mut callbacks).unwrap();
+ state = callbacks.into_state();
+ result
+ };
+ let result = Array::<Array<i64>>::try_from(result).unwrap();
+ let child = result.get(0).unwrap();
+ let reuse = requested == Allow && !shared && !retain;
+ assert_eq!(
+ state.modes,
+ vec![
+ if shared { Disallow } else { Allow },
+ if reuse { Allow } else { Disallow }
+ ]
+ );
+ assert_eq!(array_pointer(&result) == root_ptr, reuse);
+ assert_eq!(array_pointer(&child) == child_ptr, reuse);
+ assert_eq!(child.get(0).unwrap(), 2);
+ for alias in
original.map(Any::from).into_iter().chain(state.alias) {
+ let alias = Array::<Array<i64>>::try_from(alias).unwrap();
+ assert_eq!(alias.get(0).unwrap().get(0).unwrap(), 1);
+ }
+ }
+ }
+}
+
+#[test]
+fn consuming_default_descent_transfers_between_contexts_without_node_borrows()
{
+ struct Inner<'a> {
+ value: Option<MutateValue<'a>>,
+ preserve_unchanged: bool,
+ result: Any,
+ }
+ #[dispatch(mutate)]
+ impl Inner<'_> {
+ fn mutate_bool(&mut self, _: bool, ctx: &mut Mutator) -> Result<Any> {
+ let value = self.value.take().unwrap();
+ self.result = if self.preserve_unchanged {
+ ctx.default_maybe_inplace_mutate_result(self, value)?.into()
+ } else {
+ ctx.default_maybe_inplace_mutate(self, value)?
+ };
+ Ok(Any::new())
+ }
+ fn mutate_integer(&mut self, value: i64) -> i64 {
+ value + 1
+ }
+ }
+ fn transfer(
+ value: MutateValue<'_>,
+ generated: bool,
+ preserve: bool,
+ retain: bool,
+ ) -> Result<Any> {
+ let alias = retain.then(|| value.cast::<Array<i64>>().unwrap());
+ assert_eq!(value.inplace_mode(), InplaceMode::Allow);
+ // Carry the result back to the array callback: Unchanged belongs to
+ // that input, not to the nested traversal's boolean root.
+ let mut inner = Inner {
+ value: Some(value),
+ preserve_unchanged: preserve,
+ result: Any::new(),
+ };
+ let result = if generated {
+ structural_mutate(true, &mut inner)?;
+ inner.result
+ } else {
+ let mut callbacks = MutateCallbacks::new(
+ inner,
+ (
+ |_: bool, ctx: &mut CallbackMutator<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()
+ } else {
+ ctx.default_maybe_inplace_mutate(value)?
+ };
+ ctx.state_mut().result = result;
+ Ok(Any::new())
+ },
+ |value: i64, _: &mut CallbackMutator<Inner<'_>>| value + 1,
+ ),
+ );
+ structural_mutate(true, &mut callbacks)?;
+ callbacks.into_state().result
+ };
+ if let Some(alias) = alias {
+ assert_eq!(alias.get(0)?, 1);
+ }
+ Ok(result)
+ }
+ struct Outer {
+ preserve: bool,
+ retain: bool,
+ }
+ #[dispatch(mutate)]
+ impl Outer {
+ fn mutate_any(&mut self, value: MutateValue<'_>) -> Result<Any> {
+ transfer(value, true, self.preserve, self.retain)
+ }
+ }
+ for preserve in [false, true] {
+ for retain in [false, true] {
+ for generated in [false, true] {
+ let root = Array::new(vec![1_i64]);
+ let pointer = array_pointer(&root);
+ let result = if generated {
+ structural_mutate(root, &mut Outer { preserve, retain })
+ } else {
+ structural_mutate(root, |value: MutateValue<'_>, _: &mut
CallbackMutator| {
+ transfer(value, false, preserve, retain)
+ })
+ }
+ .and_then(Array::<i64>::try_from)
+ .unwrap();
+ assert_eq!(array_pointer(&result) == pointer, !retain);
+ assert_eq!(result.get(0).unwrap(), 2);
+ }
+ }
+ }
+}
+
+#[test]
+fn consuming_default_descent_preserves_unchanged_and_propagates_errors() {
+ for fail in [false, true] {
+ let root = Array::new(vec![1_i64]);
+ let pointer = array_pointer(&root);
+ let result = structural_mutate(
+ root,
+ |value: MutateValue<'_>, ctx: &mut CallbackMutator| ->
Result<UnchangedOr<Any>> {
+ if value.cast::<i64>().is_some() {
+ return if fail {
+ Err(Error::new(RUNTIME_ERROR, "child failed", ""))
+ } else {
+ Ok(UnchangedOr::unchanged())
+ };
+ }
+ let result = ctx.default_maybe_inplace_mutate_result(value)?;
+ assert!(result.is_unchanged());
+ Ok(result)
+ },
+ );
+ if fail {
+ assert!(result.err().unwrap().to_string().contains("child
failed"));
+ } else {
+ let result = Array::<i64>::try_from(result.unwrap()).unwrap();
+ assert_eq!(array_pointer(&result), pointer);
+ }
+ }
+}
+
#[test]
fn reflected_fields_use_shallow_copy_and_setters() {
let source = reflected_object();
@@ -583,7 +913,7 @@ impl GeneratedMapper {
Any::from(value + 1)
}
- fn map_any(&mut self, value: &MapValue) -> Result<Any> {
+ fn map_any(&mut self, value: &tvm_ffi::StructuralView) -> Result<Any> {
self.catch_all += 1;
Ok(value.to_owned())
}
@@ -696,15 +1026,20 @@ fn generated_mutate_dispatch_recurses_through_context() {
#[derive(Default)]
struct GeneratedDefaultingDispatch {
+ preserve_unchanged: bool,
arrays: usize,
integers: Vec<i64>,
}
#[dispatch(mutate)]
impl GeneratedDefaultingDispatch {
- fn mutate_array(&mut self, _array: Array<i64>, mutator: &mut Mutator) ->
Result<Any> {
+ fn mutate_array(&mut self, array: Array<i64>, mutator: &mut Mutator) ->
Result<Any> {
self.arrays += 1;
- mutator.default_mutate(self)
+ if self.preserve_unchanged {
+ mutator.default_mutate_result(self, &array).map(Into::into)
+ } else {
+ mutator.default_mutate(self, &array)
+ }
}
fn mutate_integer(&mut self, value: i64) -> Any {
@@ -715,14 +1050,19 @@ impl GeneratedDefaultingDispatch {
#[test]
fn generated_mutate_dispatch_can_default_recurse_from_a_typed_handler() {
- let mut mutator = GeneratedDefaultingDispatch::default();
- let mutated = structural_mutate(Array::new(vec![1i64, 2]), &mut mutator)
- .and_then(Array::<i64>::try_from)
- .unwrap();
+ for preserve_unchanged in [false, true] {
+ let mut mutator = GeneratedDefaultingDispatch {
+ preserve_unchanged,
+ ..Default::default()
+ };
+ let mutated = structural_mutate(Array::new(vec![1i64, 2]), &mut
mutator)
+ .and_then(Array::<i64>::try_from)
+ .unwrap();
- assert_eq!(mutated.iter().collect::<Vec<_>>(), vec![2, 3]);
- assert_eq!(mutator.arrays, 1);
- assert_eq!(mutator.integers, vec![1, 2]);
+ assert_eq!(mutated.iter().collect::<Vec<_>>(), vec![2, 3]);
+ assert_eq!(mutator.arrays, 1);
+ assert_eq!(mutator.integers, vec![1, 2]);
+ }
}
#[test]
@@ -832,7 +1172,7 @@ fn recursive_mutate_returns_unchanged_or_a_replacement() {
}
// Recurse into containers. Keep the unchanged marker if no child
changed.
- mutator.default_mutate_result()
+ mutator.default_mutate_result(value)
}
// No rewrite: the public entry resolves unchanged to the original array.
@@ -1061,11 +1401,11 @@ fn stateful_mutate_integer(value: i64, mutator: &mut
CallbackMutator<CallbackMut
}
fn stateful_mutate_default(
- _value: &MapValue,
+ value: &tvm_ffi::StructuralView,
mutator: &mut CallbackMutator<CallbackMutateStats>,
) -> Result<Any> {
mutator.state_mut().defaults += 1;
- mutator.default_mutate()
+ mutator.default_mutate(value)
}
#[test]
@@ -1103,13 +1443,12 @@ fn stateful_mutate_recursive(
value: &MapValue,
mutator: &mut CallbackMutator<CallbackMutateDepth>,
) -> Result<Any> {
- assert_eq!(mutator.current().type_index(), value.type_index());
{
let state = mutator.state_mut();
state.current += 1;
state.maximum = state.maximum.max(state.current);
}
- let mutated = mutator.default_mutate()?;
+ let mutated = mutator.default_mutate(value)?;
{
let state = mutator.state_mut();
state.current -= 1;
@@ -1137,7 +1476,7 @@ fn
callback_mutator_state_can_change_around_recursive_reborrows() {
}
#[test]
-fn callback_mutate_current_default_is_repeatable_copy_path() {
+fn callback_mutate_explicit_default_is_repeatable_copy_path() {
let root = Array::new(vec![1i64, 2]);
let root_pointer = array_pointer(&root);
let defaults = Cell::new(0);
@@ -1145,10 +1484,10 @@ fn
callback_mutate_current_default_is_repeatable_copy_path() {
root,
(
|value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
- |_value: &MapValue, mutator: &mut CallbackMutator| -> Result<Any> {
+ |value: &MapValue, mutator: &mut CallbackMutator| -> Result<Any> {
defaults.set(defaults.get() + 1);
- let first = mutator.default_mutate()?;
- let second = mutator.default_mutate()?;
+ let first = mutator.default_mutate(value)?;
+ let second = mutator.default_mutate(value)?;
assert_ne!(any_object_pointer(&first),
any_object_pointer(&second));
Ok(first)
},
@@ -1182,9 +1521,9 @@ 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: &MapValue, mutator: &mut CallbackMutator| {
calls.set(calls.get() + 1);
- mutator.default_mutate()
+ mutator.default_mutate(value)
},
)
.and_then(Array::<i64>::try_from)
diff --git a/rust/tvm-ffi/tests/test_structural_visit.rs
b/rust/tvm-ffi/tests/test_structural_visit.rs
index 9e73b206..e1b42cba 100644
--- a/rust/tvm-ffi/tests/test_structural_visit.rs
+++ b/rust/tvm-ffi/tests/test_structural_visit.rs
@@ -1009,7 +1009,7 @@ impl GenericDispatchProbe {
WalkResult::Advance
}
- fn walk_any(&mut self, _value: &VisitValue) -> WalkResult {
+ fn walk_any(&mut self, _value: &tvm_ffi::StructuralView) -> WalkResult {
self.catch_all += 1;
WalkResult::Advance
}