Skip to main content

tvm_ffi/extra/
structural_visit.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20//! Native Rust structural visiting.
21//!
22//! Two public layers mirror the C++ API split:
23//!
24//! * [`StructuralVisitor`] + [`structural_visit`] — the visitor drives
25//!   recursion itself, like a hand-written C++ `StructuralVisitorObj`:
26//!   [`StructuralVisitor::visit`] runs once per reached value and descends
27//!   only where it calls [`StructuralVisitor::default_visit_children`] or
28//!   [`StructuralVisitor::visit_child`]. `#[dispatch(visit)]` generates this
29//!   trait from typed `visit_*` methods.
30//! * [`WalkDispatch`] + [`structural_walk`] — observer callbacks, like C++
31//!   `StructuralWalk`: the walker recurses on its own and callbacks steer it
32//!   through the returned [`WalkResult`] (advance, skip, interrupt).
33//!
34//! Both layers thread the definition-region state explicitly: walk handlers
35//! opt in with a trailing [`DefRegionKind`] argument, and a visitor receives
36//! and forwards it when descending.
37//!
38//! Underneath both, [`VisitValue`] provides borrowed matching for typed Rust
39//! dispatch. Rust supplies a temporary `ffi.StructuralVisitor` ABI object so
40//! every type's registered `__s_visit__` hook can enumerate its children and
41//! call back into the active Rust visitor. Types without a hook fall back to
42//! reflected structural fields, matching the C++ protocol.
43
44use std::cell::Cell;
45use std::marker::PhantomData;
46use std::ops::ControlFlow;
47use std::os::raw::c_void;
48use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
49use std::ptr::NonNull;
50use std::rc::Rc;
51use std::sync::atomic::{AtomicUsize, Ordering};
52use std::sync::LazyLock;
53
54use crate::any::{Any, AnyView};
55use crate::error::{Error, Result, RUNTIME_ERROR, TYPE_ERROR};
56use crate::function::Function;
57use crate::object::{Object, ObjectArc, ObjectCore};
58use crate::reflection::TypeAttrColumn;
59use crate::tvm_ffi_sys::TVMFFIFieldFlagBitMask::{
60    kTVMFFIFieldFlagBitMaskSEqHashDefSimple, kTVMFFIFieldFlagBitMaskSEqHashDefPattern,
61    kTVMFFIFieldFlagBitMaskSEqHashIgnore,
62};
63use crate::tvm_ffi_sys::{
64    TVMFFIAny, TVMFFIByteArray, TVMFFIDefRegionKind, TVMFFIFieldInfo, TVMFFIGetTypeInfo,
65    TVMFFIObject, TVMFFISEqHashKind, TVMFFITypeAttrColumn, TVMFFITypeIndex, TVMFFITypeKeyToIndex,
66};
67
68use super::structural_common::{impl_callback_chain_tuple_arities, with_structural_error_context};
69
70const STRUCTURAL_VISIT_ATTR: &str = "__s_visit__";
71const FLAG_SEQ_HASH_IGNORE: i64 = kTVMFFIFieldFlagBitMaskSEqHashIgnore as i64;
72const FLAG_SEQ_HASH_DEF_PATTERN: i64 = kTVMFFIFieldFlagBitMaskSEqHashDefPattern as i64;
73const FLAG_SEQ_HASH_DEF_SIMPLE: i64 = kTVMFFIFieldFlagBitMaskSEqHashDefSimple as i64;
74
75/// What a callback asks the Rust walker to do with the current value.
76pub enum WalkResult {
77    /// Continue and visit this value's children.
78    Advance,
79    /// Continue without visiting this value's children or firing its exit hook.
80    Skip,
81    /// Halt the entire traversal.
82    Interrupt,
83    /// Halt the entire traversal and return a payload to the caller.
84    InterruptWith(Any),
85}
86
87impl WalkResult {
88    /// Halt traversal with an FFI-compatible payload.
89    pub fn interrupt_with<T: Into<Any>>(payload: T) -> Self {
90        Self::InterruptWith(payload.into())
91    }
92}
93
94/// Convert either an infallible or fallible typed handler result.
95///
96/// This keeps simple handlers terse while allowing a handler to return
97/// `tvm_ffi::Result<WalkResult>` and use `?`.
98pub trait IntoWalkResult {
99    fn into_walk_result(self) -> Result<WalkResult>;
100}
101
102impl IntoWalkResult for WalkResult {
103    fn into_walk_result(self) -> Result<WalkResult> {
104        Ok(self)
105    }
106}
107
108impl IntoWalkResult for Result<WalkResult> {
109    fn into_walk_result(self) -> Result<WalkResult> {
110        self
111    }
112}
113
114/// Callback order for [`structural_walk`].
115#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
116pub enum WalkOrder {
117    /// Run the typed handler before the current value's children.
118    #[default]
119    PreOrder,
120    /// Run the typed handler after the current value's children.
121    PostOrder,
122}
123
124/// Definition-region state active at the current value.
125///
126/// Reflected fields marked `SEqHashDefPattern` or
127/// `SEqHashDefSimple` override the inherited state for that field's
128/// complete recursive visit.
129#[repr(i32)]
130#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
131pub enum DefRegionKind {
132    /// The value is outside a definition region.
133    #[default]
134    None = 0,
135    /// Definitions apply recursively through the visited value.
136    Pattern = 1,
137    /// Definitions apply to the visited value alone; its type is walked as uses.
138    Simple = 2,
139}
140
141const _: () = {
142    assert!(DefRegionKind::None as i32 == TVMFFIDefRegionKind::kTVMFFIDefRegionKindNone as i32);
143    assert!(
144        DefRegionKind::Pattern as i32
145            == TVMFFIDefRegionKind::kTVMFFIDefRegionKindPattern as i32
146    );
147    assert!(
148        DefRegionKind::Simple as i32
149            == TVMFFIDefRegionKind::kTVMFFIDefRegionKindSimple as i32
150    );
151};
152
153/// Interrupt state of a traversal, mirroring C++ `ffi.VisitInterrupt`.
154///
155/// Entry points and visitor-layer calls return
156/// `Result<Option<VisitInterrupt>>`: `Ok(None)` means the (sub)graph was
157/// visited completely, `Ok(Some(..))` means a handler halted the traversal
158/// with this interrupt, and `Err` means it failed.
159pub struct VisitInterrupt {
160    /// Payload returned with the interrupt, or FFI `None` for no payload.
161    pub value: Any,
162}
163
164impl VisitInterrupt {
165    /// Interrupt carrying an FFI-compatible payload.
166    pub fn with<T: Into<Any>>(payload: T) -> Self {
167        Self {
168            value: payload.into(),
169        }
170    }
171}
172
173/// Convert a callback result into structural-visit completion state.
174#[doc(hidden)]
175pub trait IntoVisitResult {
176    fn into_visit_result(self) -> Result<Option<VisitInterrupt>>;
177}
178
179impl IntoVisitResult for () {
180    #[inline]
181    fn into_visit_result(self) -> Result<Option<VisitInterrupt>> {
182        Ok(None)
183    }
184}
185
186impl IntoVisitResult for Result<()> {
187    #[inline]
188    fn into_visit_result(self) -> Result<Option<VisitInterrupt>> {
189        self.map(|()| None)
190    }
191}
192
193impl IntoVisitResult for Option<VisitInterrupt> {
194    #[inline]
195    fn into_visit_result(self) -> Result<Option<VisitInterrupt>> {
196        Ok(self)
197    }
198}
199
200impl IntoVisitResult for Result<Option<VisitInterrupt>> {
201    #[inline]
202    fn into_visit_result(self) -> Result<Option<VisitInterrupt>> {
203        self
204    }
205}
206
207/// Fallible result returned by generated typed dispatch.
208#[doc(hidden)]
209pub type WalkCallbackResult = Result<WalkResult>;
210
211/// A borrowed view of a raw tvm-ffi value passed to structural-visit callbacks.
212///
213/// Generated visitors match this value without taking ownership: borrowed
214/// object-node handlers use [`VisitValue::as_node`], while POD or object-ref
215/// value handlers use [`VisitValue::cast`].
216pub use super::structural_common::StructuralValue as VisitValue;
217
218enum NativeHalt {
219    Interrupt(Any),
220    Error(Error),
221}
222
223impl From<Error> for NativeHalt {
224    fn from(error: Error) -> Self {
225        NativeHalt::Error(error)
226    }
227}
228
229type NativeResult = std::result::Result<(), NativeHalt>;
230
231/// State and recursive operations available to a visit callback.
232///
233/// A matched callback owns traversal of its value. Recursive operations
234/// reborrow the visitor, so mutable state cannot remain borrowed across them.
235pub struct VisitContext<'a, State> {
236    driver: &'a mut dyn VisitContextDriver<State>,
237    current: VisitValue,
238    def_region_kind: DefRegionKind,
239    _not_send_sync: PhantomData<Rc<()>>,
240}
241
242trait VisitContextDriver<State> {
243    fn state(&self) -> &State;
244    fn state_mut(&mut self) -> &mut State;
245    fn visit_raw(
246        &mut self,
247        raw: TVMFFIAny,
248        def_region_kind: DefRegionKind,
249    ) -> Result<Option<VisitInterrupt>>;
250    fn visit_children_raw(
251        &mut self,
252        raw: TVMFFIAny,
253        def_region_kind: DefRegionKind,
254    ) -> Result<Option<VisitInterrupt>>;
255}
256
257impl<State> VisitContext<'_, State> {
258    /// User state shared by every callback in this traversal.
259    pub fn state(&self) -> &State {
260        self.driver.state()
261    }
262
263    /// Mutably borrow the user state.
264    pub fn state_mut(&mut self) -> &mut State {
265        self.driver.state_mut()
266    }
267
268    /// Complete borrowed value active at this callback.
269    pub fn current(&self) -> &VisitValue {
270        &self.current
271    }
272
273    /// Definition-region state active at the callback's current value.
274    pub fn def_region_kind(&self) -> DefRegionKind {
275        self.def_region_kind
276    }
277
278    /// Visit `child` using the current definition-region state.
279    pub fn visit<T>(&mut self, child: &T) -> Result<Option<VisitInterrupt>>
280    where
281        for<'x> AnyView<'x>: From<&'x T>,
282    {
283        self.visit_with(child, self.def_region_kind)
284    }
285
286    /// Visit `child` using an explicit definition-region state.
287    pub fn visit_with<T>(
288        &mut self,
289        child: &T,
290        def_region_kind: DefRegionKind,
291    ) -> Result<Option<VisitInterrupt>>
292    where
293        for<'x> AnyView<'x>: From<&'x T>,
294    {
295        let raw = raw_of(AnyView::from(child));
296        if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
297            return Ok(None);
298        }
299        self.driver.visit_raw(raw, def_region_kind)
300    }
301
302    /// Visit the current value's children using registered hooks or reflected
303    /// structural fields. The current value itself is not dispatched again.
304    pub fn visit_children(&mut self) -> Result<Option<VisitInterrupt>> {
305        self.driver
306            .visit_children_raw(self.current.raw(), self.def_region_kind)
307    }
308}
309
310/// Conversion into the visitor argument accepted by [`structural_visit`].
311///
312/// Accepts a mutable [`StructuralVisitor`] or a first-match callback chain.
313/// Use [`VisitCallbacks`] when the chain needs mutable state.
314#[diagnostic::on_unimplemented(
315    message = "`{Self}` is not a supported `structural_visit` visitor",
316    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)",
317    note = "callback arguments need explicit type annotations; use `VisitCallbacks::new(state, callbacks)` for ordinary mutable callback state"
318)]
319pub trait IntoVisitor<Marker> {
320    #[doc(hidden)]
321    fn visit_root(self, root: TVMFFIAny) -> Result<Option<VisitInterrupt>>;
322}
323
324impl<V: StructuralVisitor> IntoVisitor<V> for &mut V {
325    fn visit_root(self, root: TVMFFIAny) -> Result<Option<VisitInterrupt>> {
326        finish(run_structural_visitor(
327            root,
328            self,
329            user_runtime_vtable::<V>(),
330        ))
331    }
332}
333
334/// One link in a first-match visitor callback chain.
335pub trait VisitChainLink<State, Marker>: visit_sealed::SealedLink<State, Marker> {
336    #[doc(hidden)]
337    fn try_visit(
338        &self,
339        value: &VisitValue,
340        visitor: &mut VisitContext<'_, State>,
341    ) -> Option<Result<Option<VisitInterrupt>>>;
342}
343
344mod visit_sealed {
345    use super::{IntoVisitResult, ObjectCore, VisitContext, VisitValue};
346
347    pub trait SealedLink<State, Marker> {}
348
349    impl<F, State, T, O> SealedLink<State, super::ByVisitOwnedLink<T>> for F
350    where
351        F: for<'visitor, 'driver> Fn(T, &'visitor mut VisitContext<'driver, State>) -> O,
352        O: IntoVisitResult,
353    {
354    }
355
356    impl<F, State, N: ObjectCore, O> SealedLink<State, super::ByVisitNodeLink<N>> for F
357    where
358        F: for<'value, 'visitor, 'driver> Fn(
359            &'value N,
360            &'visitor mut VisitContext<'driver, State>,
361        ) -> O,
362        O: IntoVisitResult,
363    {
364    }
365
366    impl<F, State, O> SealedLink<State, super::ByVisitCatchAllLink> for F
367    where
368        F: for<'value, 'visitor, 'driver> Fn(
369            &'value VisitValue,
370            &'visitor mut VisitContext<'driver, State>,
371        ) -> O,
372        O: IntoVisitResult,
373    {
374    }
375}
376
377#[doc(hidden)]
378pub struct ByVisitOwnedLink<T>(PhantomData<T>);
379
380impl<F, State, T, O> VisitChainLink<State, ByVisitOwnedLink<T>> for F
381where
382    F: for<'visitor, 'driver> Fn(T, &'visitor mut VisitContext<'driver, State>) -> O,
383    T: crate::type_traits::AnyCompatible,
384    O: IntoVisitResult,
385{
386    fn try_visit(
387        &self,
388        value: &VisitValue,
389        visitor: &mut VisitContext<'_, State>,
390    ) -> Option<Result<Option<VisitInterrupt>>> {
391        value
392            .cast::<T>()
393            .map(|typed| self(typed, visitor).into_visit_result())
394    }
395}
396
397#[doc(hidden)]
398pub struct ByVisitNodeLink<N>(PhantomData<N>);
399
400impl<F, State, N, O> VisitChainLink<State, ByVisitNodeLink<N>> for F
401where
402    F: for<'value, 'visitor, 'driver> Fn(
403        &'value N,
404        &'visitor mut VisitContext<'driver, State>,
405    ) -> O,
406    N: ObjectCore,
407    O: IntoVisitResult,
408{
409    fn try_visit(
410        &self,
411        value: &VisitValue,
412        visitor: &mut VisitContext<'_, State>,
413    ) -> Option<Result<Option<VisitInterrupt>>> {
414        value
415            .as_node::<N>()
416            .map(|node| self(node, visitor).into_visit_result())
417    }
418}
419
420#[doc(hidden)]
421pub enum ByVisitCatchAllLink {}
422
423impl<F, State, O> VisitChainLink<State, ByVisitCatchAllLink> for F
424where
425    F: for<'value, 'visitor, 'driver> Fn(
426        &'value VisitValue,
427        &'visitor mut VisitContext<'driver, State>,
428    ) -> O,
429    O: IntoVisitResult,
430{
431    fn try_visit(
432        &self,
433        value: &VisitValue,
434        visitor: &mut VisitContext<'_, State>,
435    ) -> Option<Result<Option<VisitInterrupt>>> {
436        Some(self(value, visitor).into_visit_result())
437    }
438}
439
440#[doc(hidden)]
441pub struct ByVisitChainLink<Markers>(PhantomData<fn(Markers)>);
442
443macro_rules! impl_visit_chain_link {
444    ($(($F:ident, $M:ident, $idx:tt)),+) => {
445        impl<State, $($F, $M,)+>
446            visit_sealed::SealedLink<State, ByVisitChainLink<($($M,)+)>> for ($($F,)+)
447        where
448            $($F: VisitChainLink<State, $M>,)+
449        {
450        }
451
452        impl<State, $($F, $M,)+> VisitChainLink<State, ByVisitChainLink<($($M,)+)>>
453            for ($($F,)+)
454        where
455            $($F: VisitChainLink<State, $M>,)+
456        {
457            fn try_visit(
458                &self,
459                value: &VisitValue,
460                visitor: &mut VisitContext<'_, State>,
461            ) -> Option<Result<Option<VisitInterrupt>>> {
462                $(
463                    if let Some(result) = self.$idx.try_visit(value, visitor) {
464                        return Some(result);
465                    }
466                )+
467                None
468            }
469        }
470    };
471}
472
473impl_callback_chain_tuple_arities!(impl_visit_chain_link);
474
475/// A reusable callback visitor with shared user state.
476pub struct VisitCallbacks<State, Link, Marker> {
477    state: State,
478    callbacks: Rc<Link>,
479    _marker: PhantomData<fn(Marker)>,
480}
481
482impl<State, Link, Marker> VisitCallbacks<State, Link, Marker>
483where
484    Link: VisitChainLink<State, Marker>,
485{
486    /// Construct a stateful callback visitor.
487    pub fn new(state: State, callbacks: Link) -> Self {
488        Self {
489            state,
490            callbacks: Rc::new(callbacks),
491            _marker: PhantomData,
492        }
493    }
494}
495
496impl<State, Link, Marker> VisitCallbacks<State, Link, Marker> {
497    /// Shared access to the callback state.
498    pub fn state(&self) -> &State {
499        &self.state
500    }
501
502    /// Mutable access to the callback state outside an active recursive call.
503    pub fn state_mut(&mut self) -> &mut State {
504        &mut self.state
505    }
506
507    /// Consume the visitor and return its state.
508    pub fn into_state(self) -> State {
509        self.state
510    }
511}
512
513struct DirectVisitCallbacks<'a, Link, Marker> {
514    state: (),
515    callbacks: &'a Link,
516    _marker: PhantomData<fn(Marker)>,
517}
518
519trait VisitCallbackState<State> {
520    fn callback_state(&self) -> &State;
521    fn callback_state_mut(&mut self) -> &mut State;
522}
523
524impl<State, Link, Marker> VisitCallbackState<State> for VisitCallbacks<State, Link, Marker> {
525    fn callback_state(&self) -> &State {
526        &self.state
527    }
528
529    fn callback_state_mut(&mut self) -> &mut State {
530        &mut self.state
531    }
532}
533
534impl<Link, Marker> VisitCallbackState<()> for DirectVisitCallbacks<'_, Link, Marker> {
535    fn callback_state(&self) -> &() {
536        &self.state
537    }
538
539    fn callback_state_mut(&mut self) -> &mut () {
540        &mut self.state
541    }
542}
543
544#[doc(hidden)]
545pub struct ByVisitCallbacks<Marker>(PhantomData<fn(Marker)>);
546
547impl<Link, Marker> IntoVisitor<ByVisitCallbacks<Marker>> for Link
548where
549    Link: VisitChainLink<(), Marker>,
550{
551    fn visit_root(self, root: TVMFFIAny) -> Result<Option<VisitInterrupt>> {
552        let callbacks = self;
553        let mut visitor = DirectVisitCallbacks::<Link, Marker> {
554            state: (),
555            callbacks: &callbacks,
556            _marker: PhantomData,
557        };
558        finish(run_structural_visitor(
559            root,
560            &mut visitor,
561            user_runtime_vtable::<DirectVisitCallbacks<Link, Marker>>(),
562        ))
563    }
564}
565
566// Keep the generated walk-dispatch paths stable.
567pub use super::dispatch::{ByWalkDispatch, DispatchWalker, WalkDispatch};
568
569/// Conversion into the walker argument of [`structural_walk`].
570///
571/// Accepts a mutable [`WalkDispatch`], a typed callback, or a nested callback
572/// tuple. `Marker` distinguishes the supported callback shapes.
573#[diagnostic::on_unimplemented(
574    message = "`{Self}` is not a supported `structural_walk` walker",
575    note = "accepted walkers: `&mut V` where `V: WalkDispatch`; a closure over `&VisitValue`, \
576            an FFI value type `T`, or `&N` of an object node type (`N: ObjectCore`, e.g. \
577            `&Object`), optionally with a trailing `DefRegionKind` argument; or a tuple of \
578            up to 12 such links (tuples nest, so `(a, (b, c))` chains more)",
579    note = "closure arguments need explicit type annotations; ObjectRef wrappers like `String` \
580            or `Array<T>` are FFI value types — take them by value, not by reference"
581)]
582pub trait IntoWalker<Marker> {
583    #[doc(hidden)]
584    type Walker: NativeVisit;
585    #[doc(hidden)]
586    fn into_walker(self) -> Self::Walker;
587}
588
589/// Adapter for a catch-all walk callback.
590#[doc(hidden)]
591pub struct ClosureWalker<F> {
592    callback: F,
593}
594
595impl<F, O> NativeVisit for ClosureWalker<F>
596where
597    F: FnMut(&VisitValue) -> O,
598    O: IntoWalkResult,
599{
600    fn visit(&mut self, value: &VisitValue, _def_region_kind: DefRegionKind) -> Result<WalkResult> {
601        (self.callback)(value).into_walk_result()
602    }
603}
604
605#[doc(hidden)]
606pub enum ByValueClosure {}
607
608impl<F, O> IntoWalker<ByValueClosure> for F
609where
610    F: FnMut(&VisitValue) -> O,
611    O: IntoWalkResult,
612{
613    type Walker = ClosureWalker<F>;
614    fn into_walker(self) -> Self::Walker {
615        ClosureWalker { callback: self }
616    }
617}
618
619/// Catch-all walk adapter that also supplies the definition-region state.
620#[doc(hidden)]
621pub struct ClosureKindWalker<F> {
622    callback: F,
623}
624
625impl<F, O> NativeVisit for ClosureKindWalker<F>
626where
627    F: FnMut(&VisitValue, DefRegionKind) -> O,
628    O: IntoWalkResult,
629{
630    fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) -> Result<WalkResult> {
631        (self.callback)(value, def_region_kind).into_walk_result()
632    }
633}
634
635#[doc(hidden)]
636pub enum ByValueKindClosure {}
637
638impl<F, O> IntoWalker<ByValueKindClosure> for F
639where
640    F: FnMut(&VisitValue, DefRegionKind) -> O,
641    O: IntoWalkResult,
642{
643    type Walker = ClosureKindWalker<F>;
644    fn into_walker(self) -> Self::Walker {
645        ClosureKindWalker { callback: self }
646    }
647}
648
649/// One link in a first-match [`structural_walk`] callback chain.
650///
651/// Supported links are typed values, borrowed object nodes, `&VisitValue`,
652/// and mutable [`WalkDispatch`] implementations, optionally followed by
653/// [`DefRegionKind`]. Tuples hold up to 12 links and may be nested.
654pub trait WalkChainLink<Marker>: sealed::SealedLink<Marker> {
655    /// Run this link if `value` matches its argument type; `None` hands the
656    /// value to the next link.
657    #[doc(hidden)]
658    fn try_call(
659        &mut self,
660        value: &VisitValue,
661        def_region_kind: DefRegionKind,
662    ) -> Option<WalkCallbackResult>;
663}
664
665mod sealed {
666    use super::{DefRegionKind, IntoWalkResult, ObjectCore, VisitValue, WalkDispatch};
667
668    pub trait SealedLink<Marker> {}
669
670    impl<F, T, O> SealedLink<super::ByOwnedLink<T>> for F
671    where
672        F: FnMut(T) -> O,
673        O: IntoWalkResult,
674    {
675    }
676    impl<F, T, O> SealedLink<super::ByOwnedKindLink<T>> for F
677    where
678        F: FnMut(T, DefRegionKind) -> O,
679        O: IntoWalkResult,
680    {
681    }
682    impl<F, N: ObjectCore, O> SealedLink<super::ByNodeLink<N>> for F
683    where
684        F: for<'a> FnMut(&'a N) -> O,
685        O: IntoWalkResult,
686    {
687    }
688    impl<F, N: ObjectCore, O> SealedLink<super::ByNodeKindLink<N>> for F
689    where
690        F: for<'a> FnMut(&'a N, DefRegionKind) -> O,
691        O: IntoWalkResult,
692    {
693    }
694    impl<F, O> SealedLink<super::ByCatchAllLink> for F
695    where
696        F: for<'a> FnMut(&'a VisitValue) -> O,
697        O: IntoWalkResult,
698    {
699    }
700    impl<F, O> SealedLink<super::ByCatchAllKindLink> for F
701    where
702        F: for<'a> FnMut(&'a VisitValue, DefRegionKind) -> O,
703        O: IntoWalkResult,
704    {
705    }
706    impl<V: WalkDispatch> SealedLink<super::ByWalkDispatchLink> for &mut V {}
707}
708
709#[doc(hidden)]
710pub struct ByOwnedLink<T>(PhantomData<T>);
711
712impl<F, T, O> WalkChainLink<ByOwnedLink<T>> for F
713where
714    F: FnMut(T) -> O,
715    T: crate::type_traits::AnyCompatible,
716    O: IntoWalkResult,
717{
718    #[inline]
719    fn try_call(
720        &mut self,
721        value: &VisitValue,
722        _def_region_kind: DefRegionKind,
723    ) -> Option<WalkCallbackResult> {
724        value
725            .cast::<T>()
726            .map(|typed| self(typed).into_walk_result())
727    }
728}
729
730#[doc(hidden)]
731pub struct ByOwnedKindLink<T>(PhantomData<T>);
732
733impl<F, T, O> WalkChainLink<ByOwnedKindLink<T>> for F
734where
735    F: FnMut(T, DefRegionKind) -> O,
736    T: crate::type_traits::AnyCompatible,
737    O: IntoWalkResult,
738{
739    #[inline]
740    fn try_call(
741        &mut self,
742        value: &VisitValue,
743        def_region_kind: DefRegionKind,
744    ) -> Option<WalkCallbackResult> {
745        value
746            .cast::<T>()
747            .map(|typed| self(typed, def_region_kind).into_walk_result())
748    }
749}
750
751#[doc(hidden)]
752pub struct ByNodeLink<N>(PhantomData<N>);
753
754impl<F, N, O> WalkChainLink<ByNodeLink<N>> for F
755where
756    F: for<'a> FnMut(&'a N) -> O,
757    N: ObjectCore,
758    O: IntoWalkResult,
759{
760    #[inline]
761    fn try_call(
762        &mut self,
763        value: &VisitValue,
764        _def_region_kind: DefRegionKind,
765    ) -> Option<WalkCallbackResult> {
766        value
767            .as_node::<N>()
768            .map(|node| self(node).into_walk_result())
769    }
770}
771
772#[doc(hidden)]
773pub struct ByNodeKindLink<N>(PhantomData<N>);
774
775impl<F, N, O> WalkChainLink<ByNodeKindLink<N>> for F
776where
777    F: for<'a> FnMut(&'a N, DefRegionKind) -> O,
778    N: ObjectCore,
779    O: IntoWalkResult,
780{
781    #[inline]
782    fn try_call(
783        &mut self,
784        value: &VisitValue,
785        def_region_kind: DefRegionKind,
786    ) -> Option<WalkCallbackResult> {
787        value
788            .as_node::<N>()
789            .map(|node| self(node, def_region_kind).into_walk_result())
790    }
791}
792
793#[doc(hidden)]
794pub enum ByCatchAllLink {}
795
796impl<F, O> WalkChainLink<ByCatchAllLink> for F
797where
798    F: for<'a> FnMut(&'a VisitValue) -> O,
799    O: IntoWalkResult,
800{
801    #[inline]
802    fn try_call(
803        &mut self,
804        value: &VisitValue,
805        _def_region_kind: DefRegionKind,
806    ) -> Option<WalkCallbackResult> {
807        Some(self(value).into_walk_result())
808    }
809}
810
811#[doc(hidden)]
812pub enum ByCatchAllKindLink {}
813
814impl<F, O> WalkChainLink<ByCatchAllKindLink> for F
815where
816    F: for<'a> FnMut(&'a VisitValue, DefRegionKind) -> O,
817    O: IntoWalkResult,
818{
819    #[inline]
820    fn try_call(
821        &mut self,
822        value: &VisitValue,
823        def_region_kind: DefRegionKind,
824    ) -> Option<WalkCallbackResult> {
825        Some(self(value, def_region_kind).into_walk_result())
826    }
827}
828
829#[doc(hidden)]
830pub struct ByChainLink<Markers>(PhantomData<fn(Markers)>);
831
832#[doc(hidden)]
833pub enum ByWalkDispatchLink {}
834
835impl<V: WalkDispatch> WalkChainLink<ByWalkDispatchLink> for &mut V {
836    #[inline]
837    fn try_call(
838        &mut self,
839        value: &VisitValue,
840        def_region_kind: DefRegionKind,
841    ) -> Option<WalkCallbackResult> {
842        self.dispatch_walk(value, def_region_kind)
843    }
844}
845
846/// Adapter from a [`WalkChainLink`] to the native traversal callback.
847#[doc(hidden)]
848pub struct ChainWalker<Link, Marker> {
849    link: Link,
850    marker: PhantomData<fn(Marker)>,
851}
852
853impl<Link, Marker> ChainWalker<Link, Marker> {
854    #[inline]
855    fn new(link: Link) -> Self {
856        ChainWalker {
857            link,
858            marker: PhantomData,
859        }
860    }
861}
862
863impl<Link, Marker> NativeVisit for ChainWalker<Link, Marker>
864where
865    Link: WalkChainLink<Marker>,
866{
867    #[inline]
868    fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) -> Result<WalkResult> {
869        self.link
870            .try_call(value, def_region_kind)
871            .unwrap_or(Ok(WalkResult::Advance))
872    }
873}
874
875macro_rules! impl_chain_link {
876    ($(($F:ident, $M:ident, $idx:tt)),+) => {
877        impl<$($F, $M,)+> sealed::SealedLink<ByChainLink<($($M,)+)>> for ($($F,)+)
878        where
879            $($F: WalkChainLink<$M>,)+
880        {
881        }
882
883        impl<$($F, $M,)+> WalkChainLink<ByChainLink<($($M,)+)>> for ($($F,)+)
884        where
885            $($F: WalkChainLink<$M>,)+
886        {
887            #[inline]
888            fn try_call(
889                &mut self,
890                value: &VisitValue,
891                def_region_kind: DefRegionKind,
892            ) -> Option<WalkCallbackResult> {
893                $(
894                    if let Some(result) = self.$idx.try_call(value, def_region_kind) {
895                        return Some(result);
896                    }
897                )+
898                None
899            }
900        }
901
902        impl<$($F, $M,)+> IntoWalker<($($M,)+)> for ($($F,)+)
903        where
904            $($F: WalkChainLink<$M>,)+
905        {
906            type Walker = ChainWalker<($($F,)+), ByChainLink<($($M,)+)>>;
907            fn into_walker(self) -> Self::Walker {
908                ChainWalker::new(self)
909            }
910        }
911    };
912}
913
914impl_callback_chain_tuple_arities!(impl_chain_link);
915
916macro_rules! impl_bare_link_walker {
917    ($(($marker:ident, $($fn_args:ty),+)),+ $(,)?) => {
918        $(
919            impl<F, T, O> IntoWalker<$marker<T>> for F
920            where
921                F: FnMut($($fn_args),+) -> O,
922                Self: WalkChainLink<$marker<T>>,
923                O: IntoWalkResult,
924            {
925                type Walker = ChainWalker<F, $marker<T>>;
926                fn into_walker(self) -> Self::Walker {
927                    ChainWalker::new(self)
928                }
929            }
930        )+
931    };
932}
933
934impl_bare_link_walker!(
935    (ByOwnedLink, T),
936    (ByOwnedKindLink, T, DefRegionKind),
937    (ByNodeLink, &T),
938    (ByNodeKindLink, &T, DefRegionKind),
939);
940
941/// A visitor that controls its own recursion.
942///
943/// Implementations descend with [`Self::visit_child`] or
944/// [`Self::default_visit_children`]. `#[dispatch(visit)]` generates this trait
945/// from typed `visit_*` methods.
946pub trait StructuralVisitor: Sized {
947    /// Visit one value under the definition-region state active at it.
948    fn visit(
949        &mut self,
950        value: &VisitValue,
951        def_region_kind: DefRegionKind,
952    ) -> Result<Option<VisitInterrupt>>;
953
954    /// Visit `child` under `def_region_kind`.
955    #[inline]
956    fn visit_child<T>(
957        &mut self,
958        child: &T,
959        def_region_kind: DefRegionKind,
960    ) -> Result<Option<VisitInterrupt>>
961    where
962        for<'x> AnyView<'x>: From<&'x T>,
963    {
964        let raw = raw_of(AnyView::from(child));
965        if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
966            return Ok(None);
967        }
968        let active = active_structural_visitor()?;
969        let context = std::ptr::from_mut(self).cast::<c_void>();
970        finish(with_current_visitor_context(active, context, || {
971            call_visitor(active, raw, def_region_kind)
972        }))
973    }
974
975    /// Visit `value`'s children with the default structural rules.
976    #[inline]
977    fn default_visit_children(
978        &mut self,
979        value: &VisitValue,
980        def_region_kind: DefRegionKind,
981    ) -> Result<Option<VisitInterrupt>> {
982        let raw = value.raw();
983        let context = std::ptr::from_mut(&mut *self).cast::<c_void>();
984        let result = visit_children_raw(
985            raw,
986            &mut UserChildren { visitor: self },
987            context,
988            def_region_kind,
989        )
990        .map_err(|halt| with_value_context(halt, raw));
991        finish(result)
992    }
993}
994
995fn try_visit_callbacks<State, Link, Marker>(
996    driver: &mut impl VisitContextDriver<State>,
997    callback_ptr: *const Link,
998    value: &VisitValue,
999    def_region_kind: DefRegionKind,
1000) -> Result<Option<VisitInterrupt>>
1001where
1002    Link: VisitChainLink<State, Marker>,
1003{
1004    let mut visitor = VisitContext {
1005        driver,
1006        current: VisitValue::from_raw(value.raw()),
1007        def_region_kind,
1008        _not_send_sync: PhantomData,
1009    };
1010    // SAFETY: The owning `Rc` or the direct callback's stack slot remains live
1011    // and is never modified through the driver during recursive reentry.
1012    match unsafe { (&*callback_ptr).try_visit(value, &mut visitor) } {
1013        Some(outcome) => outcome,
1014        None => visitor.visit_children(),
1015    }
1016}
1017
1018impl<State, Link, Marker> StructuralVisitor for VisitCallbacks<State, Link, Marker>
1019where
1020    Link: VisitChainLink<State, Marker>,
1021{
1022    fn visit(
1023        &mut self,
1024        value: &VisitValue,
1025        def_region_kind: DefRegionKind,
1026    ) -> Result<Option<VisitInterrupt>> {
1027        let callback_ptr = Rc::as_ptr(&self.callbacks);
1028        try_visit_callbacks::<State, Link, Marker>(self, callback_ptr, value, def_region_kind)
1029    }
1030}
1031
1032impl<Link, Marker> StructuralVisitor for DirectVisitCallbacks<'_, Link, Marker>
1033where
1034    Link: VisitChainLink<(), Marker>,
1035{
1036    fn visit(
1037        &mut self,
1038        value: &VisitValue,
1039        def_region_kind: DefRegionKind,
1040    ) -> Result<Option<VisitInterrupt>> {
1041        let callback_ptr = std::ptr::from_ref(self.callbacks);
1042        try_visit_callbacks::<(), Link, Marker>(self, callback_ptr, value, def_region_kind)
1043    }
1044}
1045
1046impl<State, Driver> VisitContextDriver<State> for Driver
1047where
1048    Driver: StructuralVisitor + VisitCallbackState<State>,
1049{
1050    fn state(&self) -> &State {
1051        self.callback_state()
1052    }
1053
1054    fn state_mut(&mut self) -> &mut State {
1055        self.callback_state_mut()
1056    }
1057
1058    fn visit_raw(
1059        &mut self,
1060        raw: TVMFFIAny,
1061        def_region_kind: DefRegionKind,
1062    ) -> Result<Option<VisitInterrupt>> {
1063        if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1064            return Ok(None);
1065        }
1066        let active = active_structural_visitor()?;
1067        let context = std::ptr::from_mut(self).cast::<c_void>();
1068        finish(with_current_visitor_context(active, context, || {
1069            call_visitor(active, raw, def_region_kind)
1070        }))
1071    }
1072
1073    fn visit_children_raw(
1074        &mut self,
1075        raw: TVMFFIAny,
1076        def_region_kind: DefRegionKind,
1077    ) -> Result<Option<VisitInterrupt>> {
1078        <Self as StructuralVisitor>::default_visit_children(
1079            self,
1080            &VisitValue::from_raw(raw),
1081            def_region_kind,
1082        )
1083    }
1084}
1085
1086/// Internal callback protocol used by [`IntoWalker`].
1087#[doc(hidden)]
1088pub trait NativeVisit {
1089    fn visit(&mut self, value: &VisitValue, def_region_kind: DefRegionKind) -> Result<WalkResult>;
1090}
1091
1092/// Action applied to each child found by the shared traversal.
1093trait ChildVisit {
1094    fn visit_child(&mut self, child: TVMFFIAny, def_region_kind: DefRegionKind) -> NativeResult;
1095}
1096
1097struct WalkChildren<'a, V, const PRE_ORDER: bool> {
1098    visitor: &'a mut V,
1099}
1100
1101impl<V: NativeVisit, const PRE_ORDER: bool> ChildVisit for WalkChildren<'_, V, PRE_ORDER> {
1102    fn visit_child(&mut self, child: TVMFFIAny, def_region_kind: DefRegionKind) -> NativeResult {
1103        visit_raw::<V, PRE_ORDER>(child, self.visitor, def_region_kind)
1104    }
1105}
1106
1107struct UserChildren<'a, V> {
1108    visitor: &'a mut V,
1109}
1110
1111impl<V: StructuralVisitor> ChildVisit for UserChildren<'_, V> {
1112    #[inline]
1113    fn visit_child(&mut self, child: TVMFFIAny, def_region_kind: DefRegionKind) -> NativeResult {
1114        if child.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1115            return Ok(());
1116        }
1117        match self
1118            .visitor
1119            .visit(&VisitValue::from_raw(child), def_region_kind)
1120        {
1121            Ok(None) => Ok(()),
1122            Ok(Some(interrupt)) => Err(NativeHalt::Interrupt(interrupt.value)),
1123            Err(error) => Err(NativeHalt::Error(error)),
1124        }
1125    }
1126}
1127
1128// Every registered container child re-enters this hot path.
1129#[inline(always)]
1130fn visit_raw<V: NativeVisit, const PRE_ORDER: bool>(
1131    value: TVMFFIAny,
1132    visitor: &mut V,
1133    def_region_kind: DefRegionKind,
1134) -> NativeResult {
1135    if value.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1136        return Ok(());
1137    }
1138
1139    let visit_value = VisitValue::from_raw(value);
1140    if PRE_ORDER {
1141        match visitor.visit(&visit_value, def_region_kind) {
1142            Ok(WalkResult::Advance) => {}
1143            Ok(WalkResult::Skip) => return Ok(()),
1144            Ok(WalkResult::Interrupt) => return Err(NativeHalt::Interrupt(Any::new())),
1145            Ok(WalkResult::InterruptWith(payload)) => return Err(NativeHalt::Interrupt(payload)),
1146            Err(error) => return Err(with_value_context(error.into(), value)),
1147        }
1148    }
1149
1150    let context = std::ptr::from_mut(&mut *visitor).cast::<c_void>();
1151    let children = &mut WalkChildren::<V, PRE_ORDER> {
1152        visitor: &mut *visitor,
1153    };
1154    if let Err(halt) = visit_children_raw(value, children, context, def_region_kind) {
1155        return Err(with_value_context(halt, value));
1156    }
1157
1158    if PRE_ORDER {
1159        Ok(())
1160    } else {
1161        match visitor.visit(&visit_value, def_region_kind) {
1162            Ok(WalkResult::Interrupt) => Err(NativeHalt::Interrupt(Any::new())),
1163            Ok(WalkResult::InterruptWith(payload)) => Err(NativeHalt::Interrupt(payload)),
1164            Ok(WalkResult::Advance | WalkResult::Skip) => Ok(()),
1165            Err(error) => Err(with_value_context(error.into(), value)),
1166        }
1167    }
1168}
1169
1170#[inline]
1171fn visit_children_raw<C: ChildVisit>(
1172    value: TVMFFIAny,
1173    visitor: &mut C,
1174    driver_context: *mut c_void,
1175    def_region_kind: DefRegionKind,
1176) -> NativeResult {
1177    if let Some(attr) =
1178        structural_visit_column().and_then(|column| column.get_raw(value.type_index))
1179    {
1180        if attr.type_index != TVMFFITypeIndex::kTVMFFINone as i32 {
1181            let active = active_structural_visitor()?;
1182            return with_current_visitor_context(active, driver_context, || {
1183                call_structural_visit_hook(active, value, def_region_kind, attr)
1184            });
1185        }
1186    }
1187
1188    if value.type_index < TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
1189        Ok(())
1190    } else {
1191        visit_reflected_fields(value, visitor, def_region_kind)
1192    }
1193}
1194
1195#[inline]
1196fn visit_reflected_fields<C: ChildVisit>(
1197    value: TVMFFIAny,
1198    visitor: &mut C,
1199    def_region_kind: DefRegionKind,
1200) -> NativeResult {
1201    let type_info = unsafe { TVMFFIGetTypeInfo(value.type_index) };
1202    if type_info.is_null() {
1203        return Err(runtime_error(&format!(
1204            "native visitor: unregistered type index {}",
1205            value.type_index
1206        ))
1207        .into());
1208    }
1209    let seq_hash_kind = unsafe {
1210        let metadata = (*type_info).metadata;
1211        if metadata.is_null() {
1212            TVMFFISEqHashKind::kTVMFFISEqHashKindUnsupported as i32
1213        } else {
1214            (*metadata).structural_eq_hash_kind
1215        }
1216    };
1217    let def_region_kind = free_var_child_region(def_region_kind, seq_hash_kind);
1218    let object = unsafe { value.data_union.v_obj } as *mut u8;
1219    let halted = unsafe {
1220        for_each_field_info(type_info, &mut |field| match visit_reflected_field(
1221            object,
1222            field,
1223            visitor,
1224            def_region_kind,
1225        ) {
1226            Ok(()) => ControlFlow::Continue(()),
1227            Err(halt) => ControlFlow::Break(halt),
1228        })
1229    };
1230    halted.map_or(Ok(()), Err)
1231}
1232
1233unsafe fn visit_reflected_field<C: ChildVisit>(
1234    object: *mut u8,
1235    field: &TVMFFIFieldInfo,
1236    visitor: &mut C,
1237    inherited_region: DefRegionKind,
1238) -> NativeResult {
1239    if field.flags & FLAG_SEQ_HASH_IGNORE != 0 {
1240        return Ok(());
1241    }
1242
1243    let Some(getter) = field.getter else {
1244        return Err(NativeHalt::Error(runtime_error(&format!(
1245            "native visitor: reflected field `{}` has no getter",
1246            field.name.as_str()
1247        ))));
1248    };
1249    let address = object.offset(field.offset as isize) as *mut c_void;
1250    // Own the getter result so partial writes and recursive borrows drop safely.
1251    let mut child = Any::new();
1252    if getter(address, Any::as_data_ptr(&mut child)) != 0 {
1253        return Err(with_error_context(
1254            NativeHalt::Error(Error::from_raised()),
1255            &format!("field `{}`", field.name.as_str()),
1256        ));
1257    }
1258
1259    let borrowed = raw_of_owned(&child);
1260    let child_region = field_def_region(field, inherited_region);
1261    visitor
1262        .visit_child(borrowed, child_region)
1263        .map_err(|halt| with_error_context(halt, &format!("field `{}`", field.name.as_str())))
1264}
1265
1266type StructuralVisitorHandle = *mut RuntimeStructuralVisitorObj;
1267type FStructuralVisit =
1268    unsafe extern "C" fn(StructuralVisitorHandle, AnyView<'static>) -> TVMFFIAny;
1269
1270/// Rust mirror of the C++ `StructuralVisitorVTable` ABI.
1271#[repr(C)]
1272struct StructuralVisitorVTable {
1273    visit: FStructuralVisit,
1274}
1275
1276/// Rust visitor object with the C++ `StructuralVisitorObj` prefix.
1277#[repr(C)]
1278struct RuntimeStructuralVisitorObj {
1279    base: Object,
1280    vtable: *const StructuralVisitorVTable,
1281    def_region_mode: i32,
1282    // The live context remains in traversal-local TLS.
1283    context_identity: *mut c_void,
1284    owner_thread: std::thread::ThreadId,
1285    panic: Option<Box<dyn std::any::Any + Send>>,
1286}
1287
1288/// Rust layout used to create and read the ABI `ffi.VisitInterrupt` object.
1289#[repr(C)]
1290struct RuntimeVisitInterruptObj {
1291    base: Object,
1292    value: Any,
1293}
1294
1295const _: () = {
1296    assert!(
1297        std::mem::offset_of!(RuntimeStructuralVisitorObj, vtable)
1298            == std::mem::size_of::<TVMFFIObject>()
1299    );
1300    assert!(
1301        std::mem::offset_of!(RuntimeStructuralVisitorObj, def_region_mode)
1302            == std::mem::size_of::<TVMFFIObject>() + std::mem::size_of::<*const c_void>()
1303    );
1304    assert!(
1305        std::mem::offset_of!(RuntimeVisitInterruptObj, value)
1306            == std::mem::size_of::<TVMFFIObject>()
1307    );
1308};
1309
1310// SAFETY: the `repr(C)` prefix and assertions above match
1311// `StructuralVisitorObj`; the runtime type is registered by the C++ extra.
1312unsafe impl ObjectCore for RuntimeStructuralVisitorObj {
1313    const TYPE_KEY: &'static str = "ffi.StructuralVisitor";
1314    const TYPE_DEPTH: i32 = Object::TYPE_DEPTH + 1;
1315
1316    fn type_index() -> i32 {
1317        static TYPE_INDEX: LazyLock<i32> = LazyLock::new(|| unsafe {
1318            let key = TVMFFIByteArray::from_str(RuntimeStructuralVisitorObj::TYPE_KEY);
1319            let mut type_index = 0;
1320            let return_code = TVMFFITypeKeyToIndex(&key, &mut type_index);
1321            if return_code != 0 {
1322                panic!(
1323                    "ffi.StructuralVisitor is not registered: {}",
1324                    Error::from_raised()
1325                );
1326            }
1327            type_index
1328        });
1329        *TYPE_INDEX
1330    }
1331
1332    unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject {
1333        Object::object_header_mut(&mut this.base)
1334    }
1335}
1336
1337// SAFETY: `VisitInterruptObj` is final and consists of `Object` followed by
1338// one `Any`, exactly matching `RuntimeVisitInterruptObj`.
1339unsafe impl ObjectCore for RuntimeVisitInterruptObj {
1340    const TYPE_KEY: &'static str = "ffi.VisitInterrupt";
1341    const TYPE_DEPTH: i32 = Object::TYPE_DEPTH + 1;
1342    const TYPE_FINAL: bool = true;
1343
1344    fn type_index() -> i32 {
1345        TVMFFITypeIndex::kTVMFFIVisitInterrupt as i32
1346    }
1347
1348    unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject {
1349        Object::object_header_mut(&mut this.base)
1350    }
1351}
1352
1353// Use a direct ABI entry for each concrete visitor type.
1354fn walk_runtime_vtable<V: NativeVisit, const PRE_ORDER: bool>() -> &'static StructuralVisitorVTable
1355{
1356    &StructuralVisitorVTable {
1357        visit: rust_vtable_walk::<V, PRE_ORDER>,
1358    }
1359}
1360
1361fn user_runtime_vtable<V: StructuralVisitor>() -> &'static StructuralVisitorVTable {
1362    &StructuralVisitorVTable {
1363        visit: rust_vtable_user::<V>,
1364    }
1365}
1366
1367struct RuntimeContextGuard {
1368    active: *mut ActiveStructuralVisitor,
1369    context: *mut c_void,
1370}
1371
1372impl Drop for RuntimeContextGuard {
1373    fn drop(&mut self) {
1374        // SAFETY: `active` points to the traversal-local state installed in
1375        // TLS, which outlives every callback guard created during that run.
1376        unsafe { (*self.active).context = self.context };
1377    }
1378}
1379
1380/// Traversal-local state exposed through TLS on the owner thread.
1381struct ActiveStructuralVisitor {
1382    visitor: StructuralVisitorHandle,
1383    context: *mut c_void,
1384    context_identity: *mut c_void,
1385}
1386
1387/// Take the Rust callback context while one vtable call is active.
1388///
1389/// # Safety
1390///
1391/// `visitor` must be null or point to a live [`RuntimeStructuralVisitorObj`].
1392#[inline(always)]
1393unsafe fn take_runtime_context(visitor: StructuralVisitorHandle) -> Result<RuntimeContextGuard> {
1394    let active = active_structural_visitor_state(visitor)
1395        .ok_or_else(|| inactive_structural_visitor_error(visitor, "callback"))?;
1396    let context = (*active).context;
1397    if context.is_null() {
1398        return Err(runtime_error(
1399            "structural visitor may only be called by its active registered hook",
1400        ));
1401    }
1402    (*active).context = std::ptr::null_mut();
1403    Ok(RuntimeContextGuard { active, context })
1404}
1405
1406unsafe extern "C" fn rust_vtable_walk<V: NativeVisit, const PRE_ORDER: bool>(
1407    visitor: StructuralVisitorHandle,
1408    value: AnyView<'static>,
1409) -> TVMFFIAny {
1410    rust_vtable_visit_impl(visitor, value, |context, raw, kind| {
1411        runtime_walk::<V, PRE_ORDER>(context, raw, kind)
1412    })
1413}
1414
1415unsafe extern "C" fn rust_vtable_user<V: StructuralVisitor>(
1416    visitor: StructuralVisitorHandle,
1417    value: AnyView<'static>,
1418) -> TVMFFIAny {
1419    rust_vtable_visit_impl(visitor, value, |context, raw, kind| {
1420        runtime_user_visit::<V>(context, raw, kind)
1421    })
1422}
1423
1424#[inline(always)]
1425unsafe fn rust_vtable_visit_impl(
1426    visitor: StructuralVisitorHandle,
1427    value: AnyView<'static>,
1428    callback: impl FnOnce(*mut c_void, TVMFFIAny, DefRegionKind) -> NativeResult,
1429) -> TVMFFIAny {
1430    let context_guard = match take_runtime_context(visitor) {
1431        Ok(guard) => guard,
1432        Err(error) => return native_result_into_raw(Err(NativeHalt::Error(error))),
1433    };
1434    let context = context_guard.context;
1435    let raw = *value.as_raw_ffi_any();
1436    let outcome = catch_unwind(AssertUnwindSafe(|| {
1437        let kind = def_region_from_raw((*visitor).def_region_mode)?;
1438        callback(context, raw, kind)
1439    }));
1440    match outcome {
1441        Ok(result) => native_result_into_raw(result),
1442        Err(payload) => {
1443            (*visitor).panic = Some(payload);
1444            native_result_into_raw(Err(NativeHalt::Error(runtime_error(
1445                "panic in structural visitor callback",
1446            ))))
1447        }
1448    }
1449}
1450
1451thread_local! {
1452    static ACTIVE_STRUCTURAL_VISITOR: Cell<*mut ActiveStructuralVisitor> = const {
1453        Cell::new(std::ptr::null_mut())
1454    };
1455}
1456
1457fn with_active_structural_visitor<T>(
1458    active_state: &mut ActiveStructuralVisitor,
1459    callback: impl FnOnce() -> T,
1460) -> T {
1461    ACTIVE_STRUCTURAL_VISITOR.with(|active| {
1462        let previous = active.replace(std::ptr::from_mut(active_state));
1463        struct Restore<'a> {
1464            active: &'a Cell<*mut ActiveStructuralVisitor>,
1465            previous: *mut ActiveStructuralVisitor,
1466        }
1467        impl Drop for Restore<'_> {
1468            fn drop(&mut self) {
1469                self.active.set(self.previous);
1470            }
1471        }
1472        let _restore = Restore { active, previous };
1473        callback()
1474    })
1475}
1476
1477fn active_structural_visitor() -> Result<StructuralVisitorHandle> {
1478    ACTIVE_STRUCTURAL_VISITOR.with(|active| {
1479        let state = active.get();
1480        if state.is_null() {
1481            Err(runtime_error(
1482                "structural visitor helper called outside structural_visit or structural_walk",
1483            ))
1484        } else {
1485            Ok(unsafe { (*state).visitor })
1486        }
1487    })
1488}
1489
1490#[inline(always)]
1491fn active_structural_visitor_state(
1492    handle: StructuralVisitorHandle,
1493) -> Option<*mut ActiveStructuralVisitor> {
1494    ACTIVE_STRUCTURAL_VISITOR.with(|active| {
1495        let state = active.get();
1496        if state.is_null() || unsafe { (*state).visitor != handle } {
1497            None
1498        } else {
1499            Some(state)
1500        }
1501    })
1502}
1503
1504#[cold]
1505fn inactive_structural_visitor_error(visitor: StructuralVisitorHandle, operation: &str) -> Error {
1506    if visitor.is_null() {
1507        return runtime_error("null active structural visitor");
1508    }
1509    // This branch is outside the hot path. The immutable owner id lets us
1510    // reject a foreign-thread call before reading context fields that the
1511    // owner thread may be updating.
1512    unsafe {
1513        if (*visitor).owner_thread != std::thread::current().id() {
1514            return runtime_error(&format!(
1515                "structural visitor {operation} invoked from a different thread"
1516            ));
1517        }
1518        if (*visitor).context_identity.is_null() {
1519            runtime_error("structural visitor was retained after its active call")
1520        } else {
1521            runtime_error(&format!(
1522                "structural visitor {operation} may only be used by its active registered hook"
1523            ))
1524        }
1525    }
1526}
1527
1528/// Expose the current Rust visitor only while a registered hook may call its
1529/// vtable. The callback hides it again before returning to Rust user code.
1530fn with_current_visitor_context(
1531    visitor: StructuralVisitorHandle,
1532    context: *mut c_void,
1533    callback: impl FnOnce() -> NativeResult,
1534) -> NativeResult {
1535    let active = active_structural_visitor_state(visitor)
1536        .ok_or_else(|| inactive_structural_visitor_error(visitor, "helper"))?;
1537    unsafe {
1538        if (*active).context_identity != context {
1539            return Err(
1540                runtime_error("structural visitor helper called on a non-active visitor").into(),
1541            );
1542        }
1543        if !(*active).context.is_null() {
1544            return Err(runtime_error("structural visitor context is already exposed").into());
1545        }
1546
1547        (*active).context = context;
1548        struct HideContext {
1549            active: *mut ActiveStructuralVisitor,
1550        }
1551        impl Drop for HideContext {
1552            fn drop(&mut self) {
1553                unsafe { (*self.active).context = std::ptr::null_mut() };
1554            }
1555        }
1556        let _hide = HideContext { active };
1557        callback()
1558    }
1559}
1560
1561#[inline(always)]
1562unsafe fn runtime_walk<V: NativeVisit, const PRE_ORDER: bool>(
1563    context: *mut c_void,
1564    raw: TVMFFIAny,
1565    def_region_kind: DefRegionKind,
1566) -> NativeResult {
1567    if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1568        return Ok(());
1569    }
1570    if raw.type_index < TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
1571        let visitor = &mut *context.cast::<V>();
1572        if PRE_ORDER {
1573            match visitor.visit(&VisitValue::from_raw(raw), def_region_kind) {
1574                Ok(WalkResult::Advance) => {}
1575                Ok(WalkResult::Skip) => return Ok(()),
1576                Ok(WalkResult::Interrupt) => return Err(NativeHalt::Interrupt(Any::new())),
1577                Ok(WalkResult::InterruptWith(payload)) => {
1578                    return Err(NativeHalt::Interrupt(payload));
1579                }
1580                Err(error) => return Err(with_value_context(error.into(), raw)),
1581            }
1582            if !has_registered_visit_hook(raw.type_index) {
1583                return Ok(());
1584            }
1585            let children = &mut WalkChildren::<V, PRE_ORDER> { visitor };
1586            return visit_children_raw(raw, children, context, def_region_kind)
1587                .map_err(|halt| with_value_context(halt, raw));
1588        }
1589        // Post-order inline values have no children unless their type
1590        // registered a visit hook. Handle the common case directly here.
1591        if !has_registered_visit_hook(raw.type_index) {
1592            return match visitor.visit(&VisitValue::from_raw(raw), def_region_kind) {
1593                Ok(WalkResult::Advance | WalkResult::Skip) => Ok(()),
1594                Ok(WalkResult::Interrupt) => Err(NativeHalt::Interrupt(Any::new())),
1595                Ok(WalkResult::InterruptWith(payload)) => Err(NativeHalt::Interrupt(payload)),
1596                Err(error) => Err(with_value_context(error.into(), raw)),
1597            };
1598        }
1599    }
1600    visit_raw::<V, PRE_ORDER>(raw, &mut *context.cast::<V>(), def_region_kind)
1601}
1602
1603#[inline(always)]
1604unsafe fn runtime_user_visit<V: StructuralVisitor>(
1605    context: *mut c_void,
1606    raw: TVMFFIAny,
1607    def_region_kind: DefRegionKind,
1608) -> NativeResult {
1609    if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1610        return Ok(());
1611    }
1612    match (&mut *context.cast::<V>()).visit(&VisitValue::from_raw(raw), def_region_kind) {
1613        Ok(None) => Ok(()),
1614        Ok(Some(interrupt)) => Err(NativeHalt::Interrupt(interrupt.value)),
1615        Err(error) => Err(NativeHalt::Error(error)),
1616    }
1617}
1618
1619fn run_structural_visitor<D>(
1620    root: TVMFFIAny,
1621    driver: &mut D,
1622    vtable: &'static StructuralVisitorVTable,
1623) -> NativeResult {
1624    let context = std::ptr::from_mut(driver).cast::<c_void>();
1625    let mut active = ObjectArc::new(RuntimeStructuralVisitorObj {
1626        base: Object::new(),
1627        vtable,
1628        def_region_mode: DefRegionKind::None as i32,
1629        context_identity: context,
1630        owner_thread: std::thread::current().id(),
1631        panic: None,
1632    });
1633    let handle = unsafe { ObjectArc::as_raw_mut(&mut active) };
1634    let mut active_state = ActiveStructuralVisitor {
1635        visitor: handle,
1636        context,
1637        context_identity: context,
1638    };
1639    // Keep all borrow-sensitive Rust state in this traversal's stack frame.
1640    // Nested callbacks validate and temporarily take it through TLS without
1641    // repeatedly touching the heap-allocated FFI object.
1642    let result = with_active_structural_visitor(&mut active_state, || {
1643        call_visitor(handle, root, DefRegionKind::None)
1644    });
1645    unsafe {
1646        (*handle).context_identity = std::ptr::null_mut();
1647    }
1648    let panic = unsafe { (*handle).panic.take() };
1649    if let Some(payload) = panic {
1650        drop(result);
1651        resume_unwind(payload);
1652    }
1653    result
1654}
1655
1656fn call_visitor(
1657    visitor: StructuralVisitorHandle,
1658    raw: TVMFFIAny,
1659    def_region_kind: DefRegionKind,
1660) -> NativeResult {
1661    if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1662        return Ok(());
1663    }
1664    if visitor.is_null() {
1665        return Err(runtime_error("no active structural visitor").into());
1666    }
1667    let callback = unsafe { (*(*visitor).vtable).visit };
1668    with_visitor_def_region(visitor, def_region_kind, || unsafe {
1669        let value = AnyView::from_raw_ffi_any(raw);
1670        visit_result_from_raw(callback(visitor, value))
1671    })
1672}
1673
1674fn call_structural_visit_hook(
1675    visitor: StructuralVisitorHandle,
1676    raw: TVMFFIAny,
1677    def_region_kind: DefRegionKind,
1678    attr: TVMFFIAny,
1679) -> NativeResult {
1680    with_visitor_def_region(visitor, def_region_kind, || unsafe {
1681        match attr.type_index {
1682            x if x == TVMFFITypeIndex::kTVMFFIOpaquePtr as i32 => {
1683                let pointer = attr.data_union.v_ptr;
1684                if pointer.is_null() {
1685                    return Err(runtime_error("structural visit hook is null").into());
1686                }
1687                // The `__s_visit__` protocol stores exactly an
1688                // `FStructuralVisit` in an opaque-pointer attribute.
1689                let hook: FStructuralVisit = std::mem::transmute(pointer);
1690                let value = AnyView::from_raw_ffi_any(raw);
1691                visit_result_from_raw(hook(visitor, value))
1692            }
1693            x if x == TVMFFITypeIndex::kTVMFFIFunction as i32 => {
1694                let function = Function::try_from(AnyView::from_raw_ffi_any(attr))?;
1695                let visitor_value = borrowed_visitor_view(visitor);
1696                let value = AnyView::from_raw_ffi_any(raw);
1697                visit_result_from_any(function.call_packed(&[visitor_value, value])?)
1698            }
1699            _ => Err(Error::new(
1700                TYPE_ERROR,
1701                "__s_visit__ must be an opaque function pointer or ffi.Function",
1702                "",
1703            )
1704            .into()),
1705        }
1706    })
1707}
1708
1709unsafe fn borrowed_visitor_view<'a>(visitor: StructuralVisitorHandle) -> AnyView<'a> {
1710    let object = visitor.cast::<TVMFFIObject>();
1711    let mut raw = TVMFFIAny::new();
1712    raw.type_index = (*object).type_index;
1713    raw.small_str_len = 0;
1714    raw.data_union.v_obj = object;
1715    AnyView::from_raw_ffi_any(raw)
1716}
1717
1718#[inline(always)]
1719fn native_result_into_raw(result: NativeResult) -> TVMFFIAny {
1720    match result {
1721        Ok(()) => TVMFFIAny::new(),
1722        Err(NativeHalt::Error(error)) => unsafe { Any::into_raw_ffi_any(Any::from(error)) },
1723        Err(NativeHalt::Interrupt(payload)) => {
1724            let interrupt = ObjectArc::new(RuntimeVisitInterruptObj {
1725                base: Object::new(),
1726                value: payload,
1727            });
1728            let object = unsafe { ObjectArc::into_raw(interrupt) }.cast_mut();
1729            let mut raw = TVMFFIAny::new();
1730            raw.type_index = TVMFFITypeIndex::kTVMFFIVisitInterrupt as i32;
1731            raw.data_union.v_obj = object.cast::<TVMFFIObject>();
1732            raw
1733        }
1734    }
1735}
1736
1737unsafe fn visit_result_from_raw(raw: TVMFFIAny) -> NativeResult {
1738    // None is the overwhelmingly common success result. Avoid constructing
1739    // and dropping an owning Any unless the hook actually stopped or failed.
1740    if raw.type_index == TVMFFITypeIndex::kTVMFFINone as i32 {
1741        Ok(())
1742    } else {
1743        visit_result_from_any(Any::from_raw_ffi_any(raw))
1744    }
1745}
1746
1747fn visit_result_from_any(value: Any) -> NativeResult {
1748    match value.type_index() {
1749        x if x == TVMFFITypeIndex::kTVMFFINone as i32 => Ok(()),
1750        x if x == TVMFFITypeIndex::kTVMFFIError as i32 => match Error::try_from(value) {
1751            Ok(error) | Err(error) => Err(NativeHalt::Error(error)),
1752        },
1753        x if x == TVMFFITypeIndex::kTVMFFIVisitInterrupt as i32 => {
1754            let raw = *value.as_raw_ffi_any();
1755            let object = unsafe { raw.data_union.v_obj };
1756            if object.is_null() {
1757                return Err(runtime_error("structural visit returned a null interrupt").into());
1758            }
1759            let payload = unsafe { (*object.cast::<RuntimeVisitInterruptObj>()).value.clone() };
1760            Err(NativeHalt::Interrupt(payload))
1761        }
1762        _ => Err(Error::new(
1763            TYPE_ERROR,
1764            "structural visit hook must return None or ffi.VisitInterrupt",
1765            "",
1766        )
1767        .into()),
1768    }
1769}
1770
1771fn with_visitor_def_region<T>(
1772    visitor: StructuralVisitorHandle,
1773    kind: DefRegionKind,
1774    callback: impl FnOnce() -> T,
1775) -> T {
1776    unsafe {
1777        let previous = (*visitor).def_region_mode;
1778        // Precedence: a pattern region propagates; entering any kind inside it has no effect.
1779        if previous == DefRegionKind::Pattern as i32 {
1780            return callback();
1781        }
1782        (*visitor).def_region_mode = kind as i32;
1783        struct Restore {
1784            visitor: StructuralVisitorHandle,
1785            previous: i32,
1786        }
1787        impl Drop for Restore {
1788            fn drop(&mut self) {
1789                unsafe { (*self.visitor).def_region_mode = self.previous };
1790            }
1791        }
1792        let _restore = Restore { visitor, previous };
1793        callback()
1794    }
1795}
1796
1797#[inline(always)]
1798fn def_region_from_raw(kind: i32) -> Result<DefRegionKind> {
1799    match kind {
1800        x if x == DefRegionKind::None as i32 => Ok(DefRegionKind::None),
1801        x if x == DefRegionKind::Pattern as i32 => Ok(DefRegionKind::Pattern),
1802        x if x == DefRegionKind::Simple as i32 => Ok(DefRegionKind::Simple),
1803        _ => Err(runtime_error("invalid structural definition-region kind")),
1804    }
1805}
1806
1807fn with_value_context(halt: NativeHalt, value: TVMFFIAny) -> NativeHalt {
1808    if value.type_index < TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32 {
1809        halt
1810    } else {
1811        with_error_context(halt, &format!("object `{}`", type_key_of(value.type_index)))
1812    }
1813}
1814
1815/// Visit `root` with a [`StructuralVisitor`] or typed callback chain.
1816///
1817/// A matching callback owns recursion; unmatched values use default child
1818/// traversal. Use [`VisitCallbacks`] to attach mutable state to the chain.
1819pub fn structural_visit<R, M>(
1820    root: &R,
1821    visitor: impl IntoVisitor<M>,
1822) -> Result<Option<VisitInterrupt>>
1823where
1824    for<'x> AnyView<'x>: From<&'x R>,
1825{
1826    visitor.visit_root(raw_of(AnyView::from(root)))
1827}
1828
1829/// Walk `root` with an observer, the Rust analog of C++
1830/// `StructuralWalk<order>(root, callbacks...)`.
1831///
1832/// `walker` is anything implementing [`IntoWalker`]: a `&mut` reference to a
1833/// stateful [`WalkDispatch`] walker (`#[dispatch(walk)]`), a bare closure
1834/// in any [`WalkChainLink`] shape (catch-all `&VisitValue`, typed, or node,
1835/// with an optional trailing [`DefRegionKind`]), or a tuple of such
1836/// callbacks tried in order — the C++ callback overloads and variadic
1837/// chain. The walker owns recursion: the handler runs once per value,
1838/// before or after the value's children according to `order`, and steers
1839/// traversal through the returned [`WalkResult`].
1840pub fn structural_walk<R, M, H>(
1841    root: &R,
1842    walker: H,
1843    order: WalkOrder,
1844) -> Result<Option<VisitInterrupt>>
1845where
1846    H: IntoWalker<M>,
1847    for<'x> AnyView<'x>: From<&'x R>,
1848{
1849    let mut dispatch = walker.into_walker();
1850    let root = raw_of(AnyView::from(root));
1851    let result = match order {
1852        WalkOrder::PreOrder => run_structural_visitor(
1853            root,
1854            &mut dispatch,
1855            walk_runtime_vtable::<H::Walker, true>(),
1856        ),
1857        WalkOrder::PostOrder => run_structural_visitor(
1858            root,
1859            &mut dispatch,
1860            walk_runtime_vtable::<H::Walker, false>(),
1861        ),
1862    };
1863    finish(result)
1864}
1865
1866fn finish(result: NativeResult) -> Result<Option<VisitInterrupt>> {
1867    match result {
1868        Ok(()) => Ok(None),
1869        Err(NativeHalt::Error(error)) => Err(error),
1870        Err(NativeHalt::Interrupt(payload)) => Ok(Some(VisitInterrupt { value: payload })),
1871    }
1872}
1873
1874#[inline]
1875pub(crate) fn field_def_region(field: &TVMFFIFieldInfo, inherited: DefRegionKind) -> DefRegionKind {
1876    // Precedence: a pattern region propagates; entering any kind inside it has no effect.
1877    if inherited == DefRegionKind::Pattern {
1878        DefRegionKind::Pattern
1879    } else if field.flags & FLAG_SEQ_HASH_DEF_SIMPLE != 0 {
1880        DefRegionKind::Simple
1881    } else if field.flags & FLAG_SEQ_HASH_DEF_PATTERN != 0 {
1882        DefRegionKind::Pattern
1883    } else {
1884        inherited
1885    }
1886}
1887
1888/// A simple definition applies to a FreeVar value itself, but not to
1889/// the FreeVar's own reflected children: nested free vars there must resolve
1890/// against an outer binding instead of rebinding. Mirrors C++
1891/// `VisitReflectedFieldsExpected`.
1892#[inline]
1893pub(crate) fn free_var_child_region(
1894    inherited: DefRegionKind,
1895    structural_eq_hash_kind: i32,
1896) -> DefRegionKind {
1897    if inherited == DefRegionKind::Simple
1898        && structural_eq_hash_kind == TVMFFISEqHashKind::kTVMFFISEqHashKindFreeVar as i32
1899    {
1900        DefRegionKind::None
1901    } else {
1902        inherited
1903    }
1904}
1905
1906fn with_error_context(halt: NativeHalt, frame: &str) -> NativeHalt {
1907    match halt {
1908        NativeHalt::Error(error) => {
1909            NativeHalt::Error(with_structural_error_context(error, "visit", frame))
1910        }
1911        interrupt => interrupt,
1912    }
1913}
1914
1915fn runtime_error(message: &str) -> Error {
1916    Error::new(RUNTIME_ERROR, message, "")
1917}
1918
1919pub(crate) fn type_attr_column(attr_name: &str) -> Option<TypeAttrColumn> {
1920    TypeAttrColumn::new(attr_name)
1921}
1922
1923/// Cached `__s_visit__` column pointer (0 = not seen yet). A registry column
1924/// is stable once created — C++ `DefaultVisitExpected` caches the same
1925/// pointer in a function-local static — while an absent column is re-queried
1926/// because a later attr registration may create it.
1927static STRUCTURAL_VISIT_COLUMN: AtomicUsize = AtomicUsize::new(0);
1928
1929#[inline]
1930fn structural_visit_column() -> Option<TypeAttrColumn> {
1931    let cached = STRUCTURAL_VISIT_COLUMN.load(Ordering::Relaxed);
1932    if cached != 0 {
1933        let pointer = cached as *mut TVMFFITypeAttrColumn;
1934        return Some(unsafe { TypeAttrColumn::from_non_null(NonNull::new_unchecked(pointer)) });
1935    }
1936    initialize_structural_visit_column()
1937}
1938
1939#[inline]
1940fn has_registered_visit_hook(type_index: i32) -> bool {
1941    structural_visit_column()
1942        .and_then(|column| column.get_raw(type_index))
1943        .is_some_and(|attr| attr.type_index != TVMFFITypeIndex::kTVMFFINone as i32)
1944}
1945
1946#[cold]
1947#[inline(never)]
1948fn initialize_structural_visit_column() -> Option<TypeAttrColumn> {
1949    let column = type_attr_column(STRUCTURAL_VISIT_ATTR)?;
1950    STRUCTURAL_VISIT_COLUMN.store(column.as_ptr() as usize, Ordering::Relaxed);
1951    Some(column)
1952}
1953
1954pub(crate) fn type_key_of(type_index: i32) -> String {
1955    unsafe {
1956        let info = TVMFFIGetTypeInfo(type_index);
1957        if info.is_null() {
1958            format!("<type_index {type_index}>")
1959        } else {
1960            (*info).type_key.as_str().to_string()
1961        }
1962    }
1963}
1964
1965/// Visit every reflected field described by `info` and its ancestors in the
1966/// same parent-to-child order as C++ `ForEachFieldInfoWithEarlyStop`.
1967///
1968/// # Safety
1969///
1970/// `info` must point to an immortal registered type-info record.
1971pub(crate) unsafe fn for_each_field_info<B>(
1972    info: *const crate::tvm_ffi_sys::TVMFFITypeInfo,
1973    callback: &mut impl FnMut(&'static TVMFFIFieldInfo) -> ControlFlow<B>,
1974) -> Option<B> {
1975    // Ancestor slot 0 is the root Object. C++ starts at slot 1, walks toward
1976    // the immediate parent, then visits the concrete type's own fields.
1977    for depth in 1..(*info).type_depth {
1978        let ancestor = *(*info).type_acenstors.offset(depth as isize);
1979        if let Some(value) = visit_field_level(ancestor, callback) {
1980            return Some(value);
1981        }
1982    }
1983    visit_field_level(info, callback)
1984}
1985
1986unsafe fn visit_field_level<B>(
1987    info: *const crate::tvm_ffi_sys::TVMFFITypeInfo,
1988    callback: &mut impl FnMut(&'static TVMFFIFieldInfo) -> ControlFlow<B>,
1989) -> Option<B> {
1990    if info.is_null() || (*info).fields.is_null() {
1991        return None;
1992    }
1993    let fields = std::slice::from_raw_parts((*info).fields, (*info).num_fields as usize);
1994    for field in fields {
1995        // C reflection tables are immortal once registered.
1996        let field: &'static TVMFFIFieldInfo = &*(field as *const TVMFFIFieldInfo);
1997        if let ControlFlow::Break(value) = callback(field) {
1998            return Some(value);
1999        }
2000    }
2001    None
2002}
2003
2004#[inline]
2005fn raw_of(view: AnyView<'_>) -> TVMFFIAny {
2006    *view.as_raw_ffi_any()
2007}
2008
2009#[inline]
2010fn raw_of_owned(any: &Any) -> TVMFFIAny {
2011    *any.as_raw_ffi_any()
2012}