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 e8eb3032 [EXTRA] Compose mutator storage and expose borrowed Rust
attributes (#918)
e8eb3032 is described below
commit e8eb3032c6ea5ee8d839d04d1a4955002fd29af0
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Nov 4 10:59:08 2026 -0500
[EXTRA] Compose mutator storage and expose borrowed Rust attributes (#918)
Allow structural map and mutate engines to compose with state-only
parents derived from `StructuralMutatorObj`. A shared engine layer owns
variable remapping and its reference lifetimes, while existing
`StructuralMapEngineBase` parents retain a single remap environment.
Expose the safe borrowed `TypeAttrColumn::get_raw_ffi_any` lookup and
use consistent `raw_ffi_any` names for Rust carrier helpers. The copied
carrier does not retain or transfer ownership; its documentation
describes registry validity and the owning `get` alternative.
---
include/tvm/ffi/extra/structural_mutate.h | 127 ++++++++++++++++------------
rust/tvm-ffi/src/extra/structural_common.rs | 6 +-
rust/tvm-ffi/src/extra/structural_map.rs | 4 +-
rust/tvm-ffi/src/extra/structural_mutate.rs | 40 ++++-----
rust/tvm-ffi/src/extra/structural_visit.rs | 2 +-
rust/tvm-ffi/src/extra/unchanged.rs | 4 +-
rust/tvm-ffi/src/reflection.rs | 16 +++-
src/ffi/extra/structural_mutate.cc | 12 +--
tests/cpp/extra/test_structural_mutate.cc | 61 +++++++++++--
9 files changed, 174 insertions(+), 98 deletions(-)
diff --git a/include/tvm/ffi/extra/structural_mutate.h
b/include/tvm/ffi/extra/structural_mutate.h
index f9fceef1..afc1a628 100644
--- a/include/tvm/ffi/extra/structural_mutate.h
+++ b/include/tvm/ffi/extra/structural_mutate.h
@@ -474,6 +474,8 @@ class StructuralMutatorObj : public Object {
public:
/*! \brief Callback-facing mutator type used by composed callback-driven
engines. */
using MutatorObjType = StructuralMutatorObj;
+ /*! \brief Empty callback-state protocol used when no custom Parent layer is
present. */
+ using StateTupleType = std::tuple<>;
/*!
* \brief Throwing form of \ref MutateExpected.
@@ -778,6 +780,24 @@ class StructuralMutatorObj : public Object {
*/
explicit StructuralMutatorObj(const StructuralMutatorVTable* vtable) :
vtable_(vtable) {}
+ /*! \brief Return the empty state tuple exposed to typed map callbacks. */
+ TVM_FFI_INLINE StateTupleType StateTuple() const noexcept { return {}; }
+
+ /*!
+ * \brief Append \p node to a failed result's mutate error context.
+ * \param result The failed result whose Error is annotated.
+ * \param node The borrowed node to name in the context.
+ */
+ TVM_FFI_COLD_CODE static void UpdateVisitErrorContext(const Expected<Any>&
result,
+ AnyView node) noexcept
{
+ // The Error is refcounted, so annotating the local handle annotates the
object the result
+ // holds. A non-object node has no context to add.
+ if (node.type_index() >= TypeIndex::kTVMFFIStaticObjectBegin) {
+ Error err = result.error();
+ ::tvm::ffi::detail::UpdateVisitErrorContext(err, node.cast<ObjectRef>());
+ }
+ }
+
/*!
* \brief Non-owning pointer to the required ABI dispatch table.
*/
@@ -1034,23 +1054,28 @@ namespace detail {
} // namespace detail
+namespace detail {
+
/*!
- * \brief Shared state and variable-remap dispatch for both structural-map
engines.
+ * \brief Engine-owned variable-remap storage above a semantic mutator Parent.
+ *
+ * Typed and dynamic map/mutate engines share this identity-substitution
environment for
+ * FreeVar and DAG identities. The Parent holds only its own descent and
callback state;
+ * remap storage belongs to this complete engine layer. Its vtable thunks
downcast to this
+ * specialization so a custom Parent need not inherit a C++ container or its
destructor.
*
- * This base owns the identity-substitution environment used for FreeVar and
DAG identities.
- * Its variable-remap vtable thunks downcast to this common subobject,
allowing the typed and
- * dynamic engines to share the environment even when a Parent layer sits
above this base.
+ * \tparam Parent Semantic layer deriving from StructuralMutatorObj and
forwarding the vtable.
*/
-class StructuralMapEngineBase : public StructuralMutatorObj {
+template <typename Parent>
+class StructuralMutatorEngineBase : public Parent {
public:
- /*! \brief Empty callback-state protocol used when no custom Parent layer is
present. */
- using StateTupleType = std::tuple<>;
+ static_assert(std::is_base_of_v<StructuralMutatorObj, Parent>,
+ "Structural map/mutate Parent must derive from
StructuralMutatorObj");
/*! \brief Construct the shared engine base with the concrete engine's
vtable. */
- explicit StructuralMapEngineBase(const StructuralMutatorVTable* vtable)
- : StructuralMutatorObj(vtable) {}
+ explicit StructuralMutatorEngineBase(const StructuralMutatorVTable* vtable)
: Parent(vtable) {}
- ~StructuralMapEngineBase() {
+ ~StructuralMutatorEngineBase() {
for (const auto& kv : var_remap_) {
detail::ObjectUnsafe::DecRefObjectHandle(
reinterpret_cast<TVMFFIObjectHandle>(const_cast<Object*>(kv.first)));
@@ -1058,9 +1083,6 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
}
protected:
- /*! \brief Return the empty state tuple exposed to typed map callbacks. */
- TVM_FFI_INLINE StateTupleType StateTuple() const noexcept { return {}; }
-
/// \cond Doxygen_Suppress
// Out of line so its strings stay out of the per-node dispatch function,
which TryLink inlines
// into. Shared by the typed and dynamic engines below.
@@ -1080,7 +1102,7 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
* \return Raw ``TVMFFIAny`` containing the owning replacement, FFI None, or
Error.
*/
static TVMFFIAny DispatchVarRemapGet(StructuralMutatorObj* mutator, AnyView
var) noexcept {
- auto* self = static_cast<StructuralMapEngineBase*>(mutator);
+ auto* self = static_cast<StructuralMutatorEngineBase*>(mutator);
return detail::ExpectedUnsafe::MoveToTVMFFIAny(self->VarRemapGetImpl(var));
}
@@ -1093,25 +1115,10 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
*/
static TVMFFIAny DispatchVarRemapSet(StructuralMutatorObj* mutator, AnyView
var,
AnyView mapped_value) noexcept {
- auto* self = static_cast<StructuralMapEngineBase*>(mutator);
+ auto* self = static_cast<StructuralMutatorEngineBase*>(mutator);
return detail::ExpectedUnsafe::MoveToTVMFFIAny(self->VarRemapSetImpl(var,
mapped_value));
}
- /*!
- * \brief Append \p node to a failed result's mutate error context.
- * \param result The failed result whose Error is annotated.
- * \param node The borrowed node to name in the context.
- */
- TVM_FFI_COLD_CODE static void UpdateVisitErrorContext(const Expected<Any>&
result,
- AnyView node) noexcept
{
- // The Error is refcounted, so annotating the local handle annotates the
object the result
- // holds. A non-object node has no context to add.
- if (node.type_index() >= TypeIndex::kTVMFFIStaticObjectBegin) {
- Error err = result.error();
- ::tvm::ffi::detail::UpdateVisitErrorContext(err, node.cast<ObjectRef>());
- }
- }
-
/*!
* \brief Look up a replacement in the identity-substitution environment.
* \param var The borrowed variable identity to look up.
@@ -1149,20 +1156,29 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
return Expected<void>();
}
- template <typename Parent, WalkOrder order, typename... Callbacks>
- friend class StructuralMapEngine;
- template <typename Parent, WalkOrder order>
- friend class StructuralMapDynEngine;
- template <typename Parent, typename... Callbacks>
- friend class StructuralMutateEngine;
- template <typename Parent>
- friend class detail::StructuralMutateDynEngine;
-
/*! \brief Identity-substitution environment keyed by object identity. */
// Raw-pointer key: IncRef once on first insert, DecRef all keys in the
destructor.
std::unordered_map<const Object*, Any> var_remap_;
};
+// Existing semantic layers may already inherit the original remap-owning
base. Reuse that
+// complete layer rather than adding a second map; new parents receive storage
above their state.
+template <typename Parent>
+using StructuralMutatorEngineParent =
+
std::conditional_t<std::is_base_of_v<StructuralMutatorEngineBase<StructuralMutatorObj>,
Parent>,
+ Parent, StructuralMutatorEngineBase<Parent>>;
+
+} // namespace detail
+
+/*!
+ * \brief Default remap-owning base retained for existing composed C++
mutators.
+ *
+ * New semantic parents can derive directly from StructuralMutatorObj; the
map/mutate engine
+ * supplies remap storage above them. Existing parents that derive from this
base keep one
+ * remap environment and their original construction/destruction behavior.
+ */
+using StructuralMapEngineBase =
detail::StructuralMutatorEngineBase<StructuralMutatorObj>;
+
/*!
* \brief Callback-dispatched structural mutator with a state-carrying Parent
layer.
*
@@ -1170,7 +1186,7 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
* type, using a compile-time-known ``as<TSub>()`` on the input node in
pre-order and on the
* descended node in post-order.
*
- * ``Parent`` derives from ``StructuralMapEngineBase``, publishes
``StateTupleType``, accepts
+ * ``Parent`` derives from ``StructuralMutatorObj``, publishes
``StateTupleType``, accepts
* and forwards the mutator vtable in its constructor, and provides a protected
* ``StateTuple() const noexcept``. Engine-internal descent uses the public
Expected entry points,
* so a Parent may override those entries directly. Each callback receives
every tuple entry
@@ -1180,7 +1196,7 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
* not virtual dispatch. This deliberately leaves composed deeper-layer
declarations eligible;
* spelling the calls as ``Parent::member`` would instead pin lookup at that
qualified layer.
* A Parent must not hide other engine-internal ``this->`` members because
matching ABI-vtable
- * paths deliberately terminate at ``StructuralMapEngineBase``.
+ * remap paths deliberately terminate at the selected engine storage layer.
*
* \tparam Parent Mutator layer that supplies descent and callback state
through the complete
* protocol above.
@@ -1188,10 +1204,10 @@ class StructuralMapEngineBase : public
StructuralMutatorObj {
* \tparam Callbacks The callbacks, tested in declaration order.
*/
template <typename Parent, WalkOrder order, typename... Callbacks>
-class StructuralMapEngine : public Parent {
+class StructuralMapEngine : public
detail::StructuralMutatorEngineParent<Parent> {
+ using EngineBase = detail::StructuralMutatorEngineParent<Parent>;
+
public:
- static_assert(std::is_base_of_v<StructuralMapEngineBase, Parent>,
- "StructuralMap Parent must derive from
StructuralMapEngineBase");
/*! \brief Tuple of state references supplied by the Parent layer. */
using StateTupleType = typename Parent::StateTupleType;
@@ -1200,7 +1216,7 @@ class StructuralMapEngine : public Parent {
* \param callbacks The typed callback links, tested in declaration order.
*/
explicit StructuralMapEngine(Callbacks... callbacks)
- : Parent(VTable()), callbacks_(std::move(callbacks)...) {}
+ : EngineBase(VTable()), callbacks_(std::move(callbacks)...) {}
private:
using ExpectedUnsafe = detail::ExpectedUnsafe;
@@ -1443,10 +1459,10 @@ class StructuralMapEngine : public Parent {
* \tparam order Callback placement relative to child mapping.
*/
template <typename Parent, WalkOrder order>
-class StructuralMapDynEngine : public Parent {
+class StructuralMapDynEngine : public
detail::StructuralMutatorEngineParent<Parent> {
+ using EngineBase = detail::StructuralMutatorEngineParent<Parent>;
+
public:
- static_assert(std::is_base_of_v<StructuralMapEngineBase, Parent>,
- "StructuralMap Parent must derive from
StructuralMapEngineBase");
/*!
* \brief Construct a dynamic map engine with the default Parent constructor.
* \param callbacks Runtime links invoked as ``callback(value)``.
@@ -1454,7 +1470,7 @@ class StructuralMapDynEngine : public Parent {
*/
StructuralMapDynEngine(Array<Tuple<int32_t, Function>> callbacks,
Array<Tuple<int32_t, Function>>
callbacks_with_def_region_kind)
- : Parent(VTable()),
+ : EngineBase(VTable()),
callbacks_(std::move(callbacks)),
callbacks_with_def_region_kind_(std::move(callbacks_with_def_region_kind)) {}
@@ -1643,14 +1659,13 @@ class StructuralMapDynEngine : public Parent {
* \tparam Callbacks Callable types whose first parameter selects the value
type.
*/
template <typename Parent, typename... Callbacks>
-class StructuralMutateEngine : public Parent {
- public:
- static_assert(std::is_base_of_v<StructuralMapEngineBase, Parent>,
- "StructuralMutate Parent must derive from
StructuralMapEngineBase");
+class StructuralMutateEngine : public
detail::StructuralMutatorEngineParent<Parent> {
+ using EngineBase = detail::StructuralMutatorEngineParent<Parent>;
+ public:
/*! \brief Construct a mutate engine over callbacks tested in declaration
order. */
explicit StructuralMutateEngine(Callbacks... callbacks)
- : Parent(VTable()), callbacks_(std::move(callbacks)...) {}
+ : EngineBase(VTable()), callbacks_(std::move(callbacks)...) {}
private:
/*! \brief Return this engine's immutable callback-aware mutator vtable. */
@@ -1861,7 +1876,7 @@ template <WalkOrder order, typename... Callbacks>
Expected<Any> StructuralMapExpected(
Any root, Callbacks&&... callbacks) noexcept { //
NOLINT(performance-unnecessary-value-param)
static_assert(sizeof...(Callbacks) != 0, "StructuralMap requires at least
one callback");
- using Mutator = StructuralMapEngine<StructuralMapEngineBase, order,
std::decay_t<Callbacks>...>;
+ using Mutator = StructuralMapEngine<StructuralMutatorObj, order,
std::decay_t<Callbacks>...>;
StructuralMutator
mutator(make_object<Mutator>(std::forward<Callbacks>(callbacks)...));
auto result = mutator->MutateExpected(root, InplaceMode::kAllow);
if (TVM_FFI_PREDICT_FALSE(result.is_err())) {
@@ -1934,7 +1949,7 @@ template <typename... Callbacks>
Expected<Any> StructuralMutateExpected(
Any root, Callbacks&&... callbacks) noexcept { //
NOLINT(performance-unnecessary-value-param)
static_assert(sizeof...(Callbacks) != 0, "StructuralMutate requires at least
one callback");
- using Mutator = StructuralMutateEngine<StructuralMapEngineBase,
std::decay_t<Callbacks>...>;
+ using Mutator = StructuralMutateEngine<StructuralMutatorObj,
std::decay_t<Callbacks>...>;
StructuralMutator
mutator(make_object<Mutator>(std::forward<Callbacks>(callbacks)...));
auto result = mutator->MutateExpected(root, InplaceMode::kAllow);
if (TVM_FFI_PREDICT_FALSE(result.is_err())) {
diff --git a/rust/tvm-ffi/src/extra/structural_common.rs
b/rust/tvm-ffi/src/extra/structural_common.rs
index 1ebc228b..19fdaca0 100644
--- a/rust/tvm-ffi/src/extra/structural_common.rs
+++ b/rust/tvm-ffi/src/extra/structural_common.rs
@@ -235,7 +235,7 @@ pub(crate) use impl_callback_chain_tuple_arities;
/// Object-backed values compare pointer identity. Both carriers must contain
/// initialized payload bits; no pointee is accessed or ownership transferred.
#[inline(always)]
-pub(crate) fn raw_tvm_ffi_any_same_as(lhs: TVMFFIAny, rhs: TVMFFIAny) -> bool {
+pub(crate) fn raw_ffi_any_same_as(lhs: TVMFFIAny, rhs: TVMFFIAny) -> bool {
lhs.type_index == rhs.type_index
&& lhs.small_str_len == rhs.small_str_len
&& unsafe { lhs.data_union.v_uint64 == rhs.data_union.v_uint64 }
@@ -248,7 +248,7 @@ pub(crate) fn raw_tvm_ffi_any_same_as(lhs: TVMFFIAny, rhs:
TVMFFIAny) -> bool {
/// Error objects as ordinary values must keep a separate result channel.
/// No payload is cloned, normalized, or released by this transfer.
#[inline(always)]
-pub(crate) fn result_into_raw_tvm_ffi_any(result: Result<Any>) -> TVMFFIAny {
+pub(crate) fn result_into_raw_ffi_any(result: Result<Any>) -> TVMFFIAny {
unsafe {
match result {
Ok(value) => Any::into_raw_ffi_any(value),
@@ -266,7 +266,7 @@ pub(crate) fn result_into_raw_tvm_ffi_any(result:
Result<Any>) -> TVMFFIAny {
/// if it was encoded from `Ok`; all other tags become an owning success.
/// Borrowed views and rvalue slots must be normalized before this transfer.
#[inline(always)]
-pub(crate) unsafe fn raw_tvm_ffi_any_into_result(raw: TVMFFIAny) ->
Result<Any> {
+pub(crate) unsafe fn raw_ffi_any_into_result(raw: TVMFFIAny) -> Result<Any> {
if raw.type_index != TVMFFITypeIndex::kTVMFFIError as i32 {
return Ok(Any::from_raw_ffi_any(raw));
}
diff --git a/rust/tvm-ffi/src/extra/structural_map.rs
b/rust/tvm-ffi/src/extra/structural_map.rs
index 02c541ab..6ba5c8ca 100644
--- a/rust/tvm-ffi/src/extra/structural_map.rs
+++ b/rust/tvm-ffi/src/extra/structural_map.rs
@@ -19,7 +19,7 @@
//! Ordered replacement callbacks and adapters for structural mapping.
-use super::structural_common::raw_tvm_ffi_any_same_as;
+use super::structural_common::raw_ffi_any_same_as;
use super::structural_mutate::{
IntoMutateResult, MutationView, StructuralMutateEngine,
StructuralMutatorView,
with_mutation_error_context,
@@ -115,7 +115,7 @@ where
};
let mapped =
callback_result.map_err(|error|
with_mutation_error_context(error, raw))?;
- if is_unchanged(&mapped) || raw_tvm_ffi_any_same_as(raw,
*mapped.as_raw_ffi_any()) {
+ if is_unchanged(&mapped) || raw_ffi_any_same_as(raw,
*mapped.as_raw_ffi_any()) {
// Drop temporary ownership before default descent checks
uniqueness.
drop(mapped);
mutator.default_mutate(self, value).map(Into::into)
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 4d77a081..83440788 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -74,8 +74,8 @@ use crate::tvm_ffi_sys::{
use crate::tvm_ffi_sys::{TVMFFIObjectHandle, TVMFFISEqHashKind};
use super::structural_common::{
- missing_field_getter_error, raw_tvm_ffi_any_into_result,
raw_tvm_ffi_any_same_as,
- result_into_raw_tvm_ffi_any, runtime_error, type_key_of,
with_structural_error_context,
+ missing_field_getter_error, raw_ffi_any_into_result, raw_ffi_any_same_as,
+ result_into_raw_ffi_any, runtime_error, type_key_of,
with_structural_error_context,
with_structural_field_error_context, with_visit_error_context,
};
use super::structural_visit::{
@@ -685,7 +685,7 @@ impl<D, Base: StructuralMutatorBase>
StructuralMutatorView<'_, D, Base> {
unsafe {
let context =
StructuralMutateEngine::<Base>::checked_dispatcher(self.runtime, dispatch);
-
raw_tvm_ffi_any_into_result(StructuralMutateEngine::<Base>::default_mutate_raw(
+
raw_ffi_any_into_result(StructuralMutateEngine::<Base>::default_mutate_raw(
self.runtime,
context,
value.value,
@@ -855,7 +855,7 @@ pub(super) fn with_mutation_error_context(error: Error,
raw: TVMFFIAny) -> Error
//
-----------------------------------------------------------------------------
fn shallow_copy(raw: TVMFFIAny) -> Result<Any> {
- let Some(attr) = SHALLOW_COPY_COLUMN.get_raw(raw.type_index) else {
+ let Some(attr) = SHALLOW_COPY_COLUMN.get_raw_ffi_any(raw.type_index) else {
return Err(missing_shallow_copy_error(
raw.type_index,
SHALLOW_COPY_ATTR,
@@ -963,7 +963,7 @@ fn object_is_unique(raw: TVMFFIAny) -> bool {
/// uniquely own each writable rvalue slot, converting it at most once. A null
/// object pointer or failed runtime conversion returns an error; successful
Error
/// objects remain ordinary values rather than entering the error channel.
-fn raw_tvm_ffi_any_to_owned_any(raw: TVMFFIAny) -> Result<Any> {
+fn raw_ffi_any_to_owned_any(raw: TVMFFIAny) -> Result<Any> {
if is_plain_inline(raw.type_index) {
return Ok(unsafe { Any::from_raw_ffi_any(raw) });
}
@@ -1115,7 +1115,7 @@ impl<Base: StructuralMutatorBase>
StructuralMutateEngine<Base> {
) {
// Map adapters attribute errors to their possibly replaced value;
// direct mutation attributes callback failures to this input.
- Some(result) => result_into_raw_tvm_ffi_any(if CALLBACK_CONTEXT {
+ Some(result) => result_into_raw_ffi_any(if CALLBACK_CONTEXT {
result.map_err(|error| with_mutation_error_context(error, raw))
} else {
result
@@ -1194,7 +1194,7 @@ impl<Base: StructuralMutatorBase>
StructuralMutateEngine<Base> {
match Self::runtime_var_remap_get(runtime, *var.as_raw_ffi_any()) {
Ok(Some(value)) => Any::into_raw_ffi_any(value),
Ok(None) => TVMFFIAny::new(),
- Err(error) => result_into_raw_tvm_ffi_any(Err(error)),
+ Err(error) => result_into_raw_ffi_any(Err(error)),
}
}
@@ -1209,9 +1209,9 @@ impl<Base: StructuralMutatorBase>
StructuralMutateEngine<Base> {
replacement: AnyView<'static>,
) -> TVMFFIAny {
// Finish replacement cleanup before constructing the ABI result.
- let result =
raw_tvm_ffi_any_to_owned_any(*replacement.as_raw_ffi_any())
+ let result = raw_ffi_any_to_owned_any(*replacement.as_raw_ffi_any())
.and_then(|value| Self::runtime_var_remap_set(runtime,
*var.as_raw_ffi_any(), &value));
- result_into_raw_tvm_ffi_any(result.map(|()| Any::new()))
+ result_into_raw_ffi_any(result.map(|()| Any::new()))
}
/// Run the public mutate/map adapter with owned `root` and borrowed
`dispatch`.
@@ -1311,8 +1311,8 @@ impl StructuralMutatorObj {
small_str_len,
data_union,
};
- result_into_raw_tvm_ffi_any(
- raw_tvm_ffi_any_into_result(result)
+ result_into_raw_ffi_any(
+ raw_ffi_any_into_result(result)
.map_err(|error| with_mutation_error_context(error, raw)),
)
}
@@ -1426,7 +1426,7 @@ impl StructuralMutatorObj {
Self::call_mutator(runtime, child_raw, InplaceMode::Disallow)
})
.map_err(|error| with_structural_field_error_context(error, "map",
field.name.as_str()))?;
- if is_unchanged(&mapped) || raw_tvm_ffi_any_same_as(child_raw,
*mapped.as_raw_ffi_any()) {
+ if is_unchanged(&mapped) || raw_ffi_any_same_as(child_raw,
*mapped.as_raw_ffi_any()) {
return Ok(());
}
@@ -1456,7 +1456,7 @@ impl StructuralMutatorObj {
};
unsafe {
let view = AnyView::from_raw_ffi_any(raw);
- raw_tvm_ffi_any_into_result(callback(mutator, view))
+ raw_ffi_any_into_result(callback(mutator, view))
}
}
@@ -1467,7 +1467,7 @@ impl StructuralMutatorObj {
fn structural_mutate_hook(raw: TVMFFIAny, mode: InplaceMode) ->
Option<TVMFFIAny> {
let use_inplace = mode == InplaceMode::Allow && object_is_unique(raw);
if use_inplace {
- if let Some(attr) =
structural_mutate_column::<true>().get_raw(raw.type_index) {
+ if let Some(attr) =
structural_mutate_column::<true>().get_raw_ffi_any(raw.type_index) {
if attr.type_index == TVMFFITypeIndex::kTVMFFIOpaquePtr as i32
|| attr.type_index == TVMFFITypeIndex::kTVMFFIFunction as
i32
{
@@ -1475,7 +1475,7 @@ impl StructuralMutatorObj {
}
}
}
- let attr =
structural_mutate_column::<false>().get_raw(raw.type_index)?;
+ let attr =
structural_mutate_column::<false>().get_raw_ffi_any(raw.type_index)?;
(attr.type_index != TVMFFITypeIndex::kTVMFFINone as
i32).then_some(attr)
}
@@ -1558,7 +1558,7 @@ impl StructuralMutatorObj {
Err(invalid_mutation_hook_error())
};
// Preserve successful Error-valued returns until the public
conversion.
- result_into_raw_tvm_ffi_any(result.map_err(|error|
with_mutation_error_context(error, raw)))
+ result_into_raw_ffi_any(result.map_err(|error|
with_mutation_error_context(error, raw)))
}
/// Borrow a live runtime mutator as an object-valued ABI argument.
@@ -1709,7 +1709,7 @@ impl StructuralMutatorObj {
small_str_len,
data_union,
};
- result_into_raw_tvm_ffi_any(
+ result_into_raw_ffi_any(
Self::mutate_without_hook::<ORDINARY_ENGINE>(runtime, raw,
def_region_kind)
.map_err(|error| with_mutation_error_context(error, raw)),
)
@@ -1723,7 +1723,7 @@ impl StructuralMutatorObj {
raw: TVMFFIAny,
) -> Result<Option<Any>> {
let callback = (*(*runtime).vtable).var_remap_get;
- let value = raw_tvm_ffi_any_into_result(callback(runtime,
AnyView::from_raw_ffi_any(raw)))?;
+ let value = raw_ffi_any_into_result(callback(runtime,
AnyView::from_raw_ffi_any(raw)))?;
Ok((value.type_index() != TVMFFITypeIndex::kTVMFFINone as
i32).then_some(value))
}
@@ -1736,7 +1736,7 @@ impl StructuralMutatorObj {
value: &Any,
) -> Result<()> {
let callback = (*(*runtime).vtable).var_remap_set;
- raw_tvm_ffi_any_into_result(callback(
+ raw_ffi_any_into_result(callback(
runtime,
AnyView::from_raw_ffi_any(raw),
AnyView::from_raw_ffi_any(*value.as_raw_ffi_any()),
@@ -1755,7 +1755,7 @@ impl StructuralMutatorObj {
def_region_kind: DefRegionKind,
) -> Result<Any> {
if raw.type_index < TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
- return raw_tvm_ffi_any_to_owned_any(raw);
+ return raw_ffi_any_to_owned_any(raw);
}
let type_info = checked_type_info(raw.type_index)?;
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index afde0d76..82d05f4e 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -695,7 +695,7 @@ impl StructuralVisitorObj {
/// owner survives descent. Hooks may not retain the runtime or input
borrow.
#[inline(always)]
unsafe fn visit_children_raw(runtime: StructuralVisitorHandle, value:
TVMFFIAny) -> TVMFFIAny {
- if let Some(attr) =
structural_visit_column().get_raw(value.type_index) {
+ if let Some(attr) =
structural_visit_column().get_raw_ffi_any(value.type_index) {
if attr.type_index != TVMFFITypeIndex::kTVMFFINone as i32 {
return Self::call_structural_visit_hook_raw(runtime, value,
attr);
}
diff --git a/rust/tvm-ffi/src/extra/unchanged.rs
b/rust/tvm-ffi/src/extra/unchanged.rs
index 631f7237..862fa6f2 100644
--- a/rust/tvm-ffi/src/extra/unchanged.rs
+++ b/rust/tvm-ffi/src/extra/unchanged.rs
@@ -21,7 +21,7 @@
use std::marker::PhantomData;
-use super::structural_common::{raw_tvm_ffi_any_same_as,
structural_error_from_raw};
+use super::structural_common::{raw_ffi_any_same_as, structural_error_from_raw};
use crate::any::{Any, AnyView, TryFromTemp};
use crate::error::{Error, Result, TYPE_ERROR};
@@ -90,7 +90,7 @@ impl<T: ContainerElement> UnchangedOr<T> {
unsafe {
T::container_copy_to_any_view(original, &mut raw);
}
- raw_tvm_ffi_any_same_as(raw, *self.data.as_raw_ffi_any())
+ raw_ffi_any_same_as(raw, *self.data.as_raw_ffi_any())
}
/// Move out the replacement, returning None when unchanged.
diff --git a/rust/tvm-ffi/src/reflection.rs b/rust/tvm-ffi/src/reflection.rs
index 019573d5..0de590f7 100644
--- a/rust/tvm-ffi/src/reflection.rs
+++ b/rust/tvm-ffi/src/reflection.rs
@@ -61,16 +61,26 @@ impl TypeAttrColumn {
/// Return an owning copy of this attribute for `type_index`.
pub fn get(self, type_index: i32) -> Option<Any> {
- let raw = self.get_raw(type_index)?;
+ let raw = self.get_raw_ffi_any(type_index)?;
if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
return None;
}
Some(Any::from(unsafe { AnyView::from_raw_ffi_any(raw) }))
}
- /// Copy one borrowed cell without taking ownership.
+ /// Copy the borrowed raw carrier for this attribute and `type_index`.
+ ///
+ /// This does not retain the value or transfer ownership. The
registry-owned
+ /// value must remain valid while the returned carrier is used, and
registration
+ /// must not race with reads. Passing the carrier to unsafe APIs still
requires
+ /// satisfying their lifetime and ownership contracts. Use [`Self::get`]
for an
+ /// owning [`Any`].
+ ///
+ /// Returns `None` when the index is outside the column or the column has
no data.
+ /// An in-range cell with the `kTVMFFINone` tag is returned as `Some`,
unlike
+ /// [`Self::get`], which filters it out.
#[inline(always)]
- pub(crate) fn get_raw(self, type_index: i32) -> Option<TVMFFIAny> {
+ pub fn get_raw_ffi_any(self, type_index: i32) -> Option<TVMFFIAny> {
unsafe {
let column = self.0.as_ref();
let index = type_index - column.begin_index;
diff --git a/src/ffi/extra/structural_mutate.cc
b/src/ffi/extra/structural_mutate.cc
index f133d28f..72ffc6ad 100644
--- a/src/ffi/extra/structural_mutate.cc
+++ b/src/ffi/extra/structural_mutate.cc
@@ -55,11 +55,11 @@ Expected<Any> StructuralMapExpected(
const Array<Tuple<int32_t, Function>>& callbacks_with_def_region_kind, int
order) noexcept {
Expected<UnchangedOr<Any>> result = [&]() -> Expected<UnchangedOr<Any>> {
if (order == static_cast<int>(WalkOrder::kPreOrder)) {
- using Mutator = StructuralMapDynEngine<StructuralMapEngineBase,
WalkOrder::kPreOrder>;
+ using Mutator = StructuralMapDynEngine<StructuralMutatorObj,
WalkOrder::kPreOrder>;
StructuralMutator mutator(make_object<Mutator>(callbacks,
callbacks_with_def_region_kind));
return mutator->MutateExpected(root, InplaceMode::kAllow);
}
- using Mutator = StructuralMapDynEngine<StructuralMapEngineBase,
WalkOrder::kPostOrder>;
+ using Mutator = StructuralMapDynEngine<StructuralMutatorObj,
WalkOrder::kPostOrder>;
StructuralMutator mutator(make_object<Mutator>(callbacks,
callbacks_with_def_region_kind));
return mutator->MutateExpected(root, InplaceMode::kAllow);
}();
@@ -77,10 +77,12 @@ Expected<Any> StructuralMapExpected(
/*! \brief Runtime counterpart of the typed callback-owned mutate engine. */
template <typename Parent>
-class StructuralMutateDynEngine : public Parent {
+class StructuralMutateDynEngine : public StructuralMutatorEngineParent<Parent>
{
+ using EngineBase = StructuralMutatorEngineParent<Parent>;
+
public:
explicit StructuralMutateDynEngine(Array<Tuple<int32_t, Function, bool>>
callbacks)
- : Parent(VTable()), callbacks_(std::move(callbacks)) {}
+ : EngineBase(VTable()), callbacks_(std::move(callbacks)) {}
private:
static const StructuralMutatorVTable* VTable() {
@@ -153,7 +155,7 @@ class StructuralMutateDynEngine : public Parent {
Expected<Any> StructuralMutateExpected(
Any root, // NOLINT(performance-unnecessary-value-param)
const Array<Tuple<int32_t, Function, bool>>& callbacks) noexcept {
- using Mutator = StructuralMutateDynEngine<StructuralMapEngineBase>;
+ using Mutator = StructuralMutateDynEngine<StructuralMutatorObj>;
StructuralMutator mutator(make_object<Mutator>(callbacks));
auto result = mutator->MutateExpected(root, InplaceMode::kAllow);
if (TVM_FFI_PREDICT_FALSE(result.is_err())) {
diff --git a/tests/cpp/extra/test_structural_mutate.cc
b/tests/cpp/extra/test_structural_mutate.cc
index 1a25b87d..0acf0257 100644
--- a/tests/cpp/extra/test_structural_mutate.cc
+++ b/tests/cpp/extra/test_structural_mutate.cc
@@ -333,12 +333,12 @@ struct MutateCount {
int maybe_inplace_expected = 0;
};
-class StructuralMapWithMutateCount : public StructuralMapEngineBase {
+class StructuralMapWithMutateCount : public StructuralMutatorObj {
public:
using StateTupleType = std::tuple<const MutateCount&, const int&>;
explicit StructuralMapWithMutateCount(const StructuralMutatorVTable* vtable)
- : StructuralMapEngineBase(vtable) {}
+ : StructuralMutatorObj(vtable) {}
const MutateCount& count() const { return count_; }
@@ -350,7 +350,7 @@ class StructuralMapWithMutateCount : public
StructuralMapEngineBase {
} else {
++count_.mutate_expected;
}
- return StructuralMapEngineBase::DefaultMutateExpected(value, inplace_mode);
+ return StructuralMutatorObj::DefaultMutateExpected(value, inplace_mode);
}
protected:
@@ -361,16 +361,65 @@ class StructuralMapWithMutateCount : public
StructuralMapEngineBase {
int marker_ = 17;
};
-class StructuralMutateLayer : public StructuralMapEngineBase {
+class StructuralMutateLayer : public StructuralMutatorObj {
public:
using MutatorObjType = StructuralMutateLayer;
explicit StructuralMutateLayer(const StructuralMutatorVTable* vtable)
- : StructuralMapEngineBase(vtable) {}
+ : StructuralMutatorObj(vtable) {}
- int callback_tag() const { return 23; }
+ int callback_tag() const { return callback_tag_; }
+
+ private:
+ int callback_tag_ = 23;
};
+TEST(StructuralMutate, EngineOwnsRemapAboveParentState) {
+ auto check = [](auto make_engine) {
+ TVar key("key");
+ TVar first("first");
+ TVar second("second");
+ {
+ auto engine = make_engine();
+ auto* mutator = static_cast<StructuralMutatorObj*>(engine.get());
+ EXPECT_EQ(mutator->VarRemapGetExpected(key).value(), nullptr);
+ mutator->VarRemapSetExpected(key, first).value();
+ EXPECT_EQ(key.use_count(), 2);
+ EXPECT_EQ(first.use_count(), 2);
+ EXPECT_TRUE(mutator->VarRemapGetExpected(key).value().same_as(first));
+ mutator->VarRemapSetExpected(key, second).value();
+ EXPECT_EQ(key.use_count(), 2);
+ EXPECT_EQ(first.use_count(), 1);
+ EXPECT_EQ(second.use_count(), 2);
+ EXPECT_TRUE(mutator->VarRemapGetExpected(key).value().same_as(second));
+ EXPECT_EQ(mutator->VarRemapSetExpected(1, second).error().kind(),
"TypeError");
+ }
+ EXPECT_EQ(key.use_count(), 1);
+ EXPECT_EQ(first.use_count(), 1);
+ EXPECT_EQ(second.use_count(), 1);
+ };
+ check([] {
+ auto callback = [](int64_t, StructuralMutatorObj*) -> Expected<Any> {
return Unchanged(); };
+ return make_object<StructuralMutateEngine<StructuralMapEngineBase,
decltype(callback)>>(
+ callback);
+ });
+ check([] {
+ auto callback = [](int64_t, StructuralMutateLayer*) -> Expected<Any> {
return Unchanged(); };
+ return make_object<StructuralMutateEngine<StructuralMutateLayer,
decltype(callback)>>(callback);
+ });
+ check([] {
+ auto callback = [](int64_t, const MutateCount&, const int&) ->
Expected<Any> {
+ return Unchanged();
+ };
+ return make_object<StructuralMapEngine<StructuralMapWithMutateCount,
WalkOrder::kPostOrder,
+ decltype(callback)>>(callback);
+ });
+ check([] {
+ return make_object<StructuralMapDynEngine<StructuralMapWithMutateCount,
WalkOrder::kPreOrder>>(
+ Array<Tuple<int32_t, Function>>(), Array<Tuple<int32_t, Function>>());
+ });
+}
+
TEST(StructuralMap, ParentLayerOwnsBothDescentsAndProvidesState) {
std::vector<int> callback_counts;
auto identity = [&](const AnyArray& value, const MutateCount& live_count,
const int& live_marker,