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 63879674 [FIX][RUST] Wait for fixture registration in structural visit
tests (#743)
63879674 is described below
commit 6387967418b6db36dd19292f720989108f36281d
Author: Shushi Hong <[email protected]>
AuthorDate: Fri Sep 4 21:54:41 2026 -0400
[FIX][RUST] Wait for fixture registration in structural visit tests (#743)
Complete the registration synchronization from #710 by making every
structural-visit test wait for fixture initialization before reading
reflection data. This prevents hook-column relocation from racing with
built-in Array and Map traversal.
Also check that the panicking callback was entered and distinguish an
error return from normal completion.
---
rust/tvm-ffi/tests/test_structural_visit.rs | 63 ++++++++++++++++++++++++++---
1 file changed, 58 insertions(+), 5 deletions(-)
diff --git a/rust/tvm-ffi/tests/test_structural_visit.rs
b/rust/tvm-ffi/tests/test_structural_visit.rs
index 9a4ada40..a11267f4 100644
--- a/rust/tvm-ffi/tests/test_structural_visit.rs
+++ b/rust/tvm-ffi/tests/test_structural_visit.rs
@@ -223,8 +223,9 @@ fn registered_primitive_visit_hook(args: &[AnyView<'_>]) ->
Result<Any> {
}
// The runtime type table leaves registration synchronization to its callers.
-// Run every fixture registration through one thread-safe initializer because
-// Rust tests execute in parallel.
+// Every test, including those using only built-in types, must wait for this
+// initializer before reading reflection data: registering a custom __s_visit__
+// hook can relocate the column containing the built-in Array/Map hooks.
static REGISTER_TEST_TYPES: LazyLock<()> = LazyLock::new(|| {
let type_index = register_visit_type(
RustVisitHookObj::TYPE_KEY,
@@ -343,6 +344,7 @@ fn runtime_error(message: &str) -> Error {
#[test]
fn public_reflection_access_uses_registered_field_and_type_attr() {
+ test_prelude();
let root = rust_visit_hook(FfiString::from("owned field"), 99i64);
let type_index = RustVisitHookObj::type_index();
@@ -362,6 +364,7 @@ fn
public_reflection_access_uses_registered_field_and_type_attr() {
#[test]
fn plain_walk_uses_registered_array_hook() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let mut integers = 0;
assert!(structural_walk(
@@ -381,6 +384,7 @@ fn plain_walk_uses_registered_array_hook() {
#[test]
fn reflected_getter_releases_partial_result_on_error() {
+ test_prelude();
let tracked = FfiString::from("a reference-counted reflected visit field");
let root = rust_visit_failing_getter(tracked.clone());
let count_before = AnyView::from(&tracked).debug_strong_count();
@@ -403,6 +407,7 @@ fn reflected_getter_releases_partial_result_on_error() {
#[test]
fn plain_walk_visits_map_values_without_visiting_keys() {
+ test_prelude();
let root: Map<FfiString, i64> = [(FfiString::from("a"), 1i64),
(FfiString::from("b"), 2i64)]
.into_iter()
.collect();
@@ -428,6 +433,7 @@ fn plain_walk_visits_map_values_without_visiting_keys() {
#[test]
fn registered_function_hook_controls_children_interrupts_and_lifetime() {
+ test_prelude();
RETAINED_VISITOR.with(|retained| {
retained.take();
});
@@ -619,6 +625,7 @@ fn primitive_hook_fast_path_preserves_pre_and_post_order() {
#[test]
fn primitive_fast_path_preserves_none_interrupt_and_error() {
+ test_prelude();
let mut none_calls = 0;
assert!(structural_walk(
&Any::new(),
@@ -654,6 +661,7 @@ fn primitive_fast_path_preserves_none_interrupt_and_error()
{
#[test]
fn registered_map_hook_visits_all_values_without_visiting_keys() {
+ test_prelude();
// More than 4 entries forces the dense (block + iteration list) layout.
let root: Map<FfiString, i64> = (0..9)
.map(|i| (FfiString::from(format!("k{i}")), i as i64))
@@ -680,6 +688,7 @@ fn
registered_map_hook_visits_all_values_without_visiting_keys() {
#[test]
fn interrupt_payload_crosses_map_traversal() {
+ test_prelude();
let root: Map<FfiString, i64> = [(FfiString::from("a"), 1i64),
(FfiString::from("b"), 2i64)]
.into_iter()
.collect();
@@ -702,6 +711,7 @@ fn interrupt_payload_crosses_map_traversal() {
#[test]
fn handler_error_crosses_map_traversal() {
+ test_prelude();
let root: Map<FfiString, i64> = [(FfiString::from("a"),
1i64)].into_iter().collect();
let error = match structural_walk(
&root,
@@ -723,6 +733,7 @@ fn handler_error_crosses_map_traversal() {
#[test]
fn interrupt_stops_without_running_remaining_callbacks() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let mut integers = 0;
let outcome = structural_walk(
@@ -774,6 +785,7 @@ impl StructuralVisitor for ManualRegionVisitor {
#[test]
fn manual_child_visit_can_override_def_region() {
+ test_prelude();
let root = Array::new(vec![7i64, 8]);
let mut probe = ManualRegionVisitor::default();
assert!(structural_visit(&root, &mut probe).unwrap().is_none());
@@ -797,6 +809,7 @@ impl GeneratedLeafVisitor {
#[test]
fn generated_visitor_defaults_unmatched_values() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut visitor = GeneratedLeafVisitor::default();
assert!(structural_visit(&root, &mut visitor).unwrap().is_none());
@@ -838,6 +851,7 @@ impl GeneratedRecursiveVisitor {
#[test]
fn generated_visitor_can_drive_recursion_through_mut_self() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let mut visitor = GeneratedRecursiveVisitor::default();
let interrupt = structural_visit(&root, &mut visitor).unwrap().unwrap();
@@ -874,6 +888,7 @@ impl GenericDispatchProbe {
#[test]
fn generated_dispatch_supports_pod_and_ordered_catch_all() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut probe = GenericDispatchProbe::default();
assert!(structural_walk(&root, &mut probe, WalkOrder::PreOrder)
@@ -919,6 +934,7 @@ impl StructuralVisitor for StraddleVisitor {
#[test]
fn visitor_can_straddle_default_children() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut probe = StraddleVisitor::default();
assert!(structural_visit(&root, &mut probe).unwrap().is_none());
@@ -955,6 +971,7 @@ impl OrderProbe {
#[test]
fn stateful_structural_walk_supports_post_order() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut probe = OrderProbe::default();
assert!(structural_walk(&root, &mut probe, WalkOrder::PostOrder)
@@ -965,6 +982,7 @@ fn stateful_structural_walk_supports_post_order() {
#[test]
fn nested_walk_restores_the_outer_active_visitor() {
+ test_prelude();
let outer = Array::new(vec![10i64, 20]);
let inner = Array::new(vec![1i64, 2]);
let mut entered_inner = false;
@@ -1002,6 +1020,7 @@ fn nested_walk_restores_the_outer_active_visitor() {
#[test]
fn interrupt_payload_is_returned_to_the_caller() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let outcome = structural_walk(
&root,
@@ -1022,6 +1041,7 @@ fn interrupt_payload_is_returned_to_the_caller() {
#[test]
fn handler_errors_include_native_visit_path() {
+ test_prelude();
let root = Array::new(vec![1i64]);
let error = match structural_walk(
&root,
@@ -1043,6 +1063,7 @@ fn handler_errors_include_native_visit_path() {
#[test]
fn visitor_errors_include_native_visit_path() {
+ test_prelude();
struct FailingVisitor;
impl StructuralVisitor for FailingVisitor {
@@ -1081,6 +1102,7 @@ fn visitor_errors_include_native_visit_path() {
#[test]
fn callback_panics_resume_after_the_registered_hook_returns() {
+ test_prelude();
let root = Array::new(vec![1i64]);
let panic = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(||
{
structural_walk(
@@ -1101,6 +1123,7 @@ fn
callback_panics_resume_after_the_registered_hook_returns() {
#[test]
fn visitor_interrupt_propagates_through_default_children() {
+ test_prelude();
struct InterruptingVisitor;
impl StructuralVisitor for InterruptingVisitor {
@@ -1128,6 +1151,7 @@ fn
visitor_interrupt_propagates_through_default_children() {
#[test]
fn closure_walk_receives_def_region_kind() {
+ test_prelude();
// C++: StructuralWalk<kPreOrder>(root,
// [&](const TVarObj* var, TVMFFIDefRegionKind kind) { ... })
let root = Array::new(vec![1i64, 2]);
@@ -1149,6 +1173,7 @@ fn closure_walk_receives_def_region_kind() {
#[test]
fn closure_walk_supports_post_order_and_skip() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut order_probe = Vec::new();
assert!(structural_walk(
@@ -1188,6 +1213,7 @@ fn closure_walk_supports_post_order_and_skip() {
// ---------------------------------------------------------------------------
#[test]
fn chain_accepts_owned_object_ref_links() {
+ test_prelude();
let root = Array::new(vec![Array::new(vec![1i64]), Array::new(vec![2i64,
3])]);
let mut lengths = Vec::new();
assert!(structural_walk(
@@ -1210,6 +1236,7 @@ fn chain_accepts_owned_object_ref_links() {
#[test]
fn chain_links_may_mix_def_region_arity() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut kinds = Vec::new();
let mut objects = 0;
@@ -1236,6 +1263,7 @@ fn chain_links_may_mix_def_region_arity() {
#[test]
fn chain_links_can_skip_children() {
+ test_prelude();
let root = Array::new(vec![Array::new(vec![1i64]),
Array::new(vec![2i64])]);
let mut arrays = 0;
let mut integers = 0;
@@ -1261,6 +1289,7 @@ fn chain_links_can_skip_children() {
#[test]
fn chain_link_errors_include_native_visit_path() {
+ test_prelude();
let root = Array::new(vec![1i64]);
let error = match structural_walk(
&root,
@@ -1279,6 +1308,7 @@ fn chain_link_errors_include_native_visit_path() {
#[test]
fn chain_supports_post_order() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let events = std::cell::RefCell::new(Vec::new());
assert!(structural_walk(
@@ -1315,6 +1345,7 @@ impl ObjectCounter {
#[test]
fn chain_splices_dispatch_walkers_between_closures() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut counter = ObjectCounter::default();
let mut integers = 0;
@@ -1334,6 +1365,7 @@ fn chain_splices_dispatch_walkers_between_closures() {
#[test]
fn chain_supports_full_arity() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let mut integers = Vec::new();
let mut objects = 0;
@@ -1374,6 +1406,7 @@ fn chain_supports_full_arity() {
#[test]
fn typed_lambda_walks_bare_and_as_single_link_tuple() {
+ test_prelude();
// A lone typed handler needs no tuple: unmatched values (the array
// itself) advance normally. The 1-tuple spelling routes through the
// chain impls instead and must agree.
@@ -1405,6 +1438,7 @@ fn typed_lambda_walks_bare_and_as_single_link_tuple() {
#[test]
fn bare_node_lambda_takes_def_region_kind() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut objects = 0;
assert!(structural_walk(
@@ -1445,6 +1479,7 @@ impl StructuralVisitor for InheritedRegionProbe {
#[test]
fn def_region_is_inherited_through_containers() {
+ test_prelude();
let root = Array::new(vec![Array::new(vec![1i64, 2])]);
let mut probe = InheritedRegionProbe {
at_root: true,
@@ -1564,6 +1599,7 @@ fn
non_recursive_region_is_clamped_for_free_var_children_only() {
#[test]
fn nested_tuple_chain_exceeds_flat_arity() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let mut integers = Vec::new();
let mut objects = 0;
@@ -1614,6 +1650,7 @@ fn nested_tuple_chain_exceeds_flat_arity() {
#[test]
fn nested_tuple_first_match_order_is_flattened() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let mut first = 0;
let mut second = 0;
@@ -1639,6 +1676,7 @@ fn nested_tuple_first_match_order_is_flattened() {
#[test]
fn callback_visit_defaults_only_when_no_link_matches() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let integers = Cell::new(0);
assert!(
@@ -1686,6 +1724,7 @@ fn stateful_visit_integer(value: i64, visitor: &mut
VisitContext<'_, StatefulVis
#[test]
fn stateful_callback_visit_uses_ordinary_mutable_state() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let mut visitor = VisitCallbacks::new(
StatefulVisitStats::default(),
@@ -1710,6 +1749,7 @@ struct StatefulVisitDepth {
#[test]
fn stateful_callback_visit_reborrows_visitor_during_recursion() {
+ test_prelude();
let root = Array::new(vec![Array::new(vec![1i64, 2])]);
let mut visitor = VisitCallbacks::new(
StatefulVisitDepth::default(),
@@ -1733,6 +1773,7 @@ fn
stateful_callback_visit_reborrows_visitor_during_recursion() {
#[test]
fn callback_visit_can_reenter_the_same_fn_through_visitor() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let visits = Cell::new(0);
assert!(structural_visit(
@@ -1749,6 +1790,7 @@ fn
callback_visit_can_reenter_the_same_fn_through_visitor() {
#[test]
fn callback_visit_tuple_is_first_match_and_can_interrupt() {
+ test_prelude();
let root = Array::new(vec![1i64, 2, 3]);
let fallback = Cell::new(0);
let interrupted = structural_visit(
@@ -1814,6 +1856,7 @@ fn
callback_visit_supports_node_links_nested_tuples_and_def_regions() {
#[test]
fn callback_visit_with_overrides_child_def_region() {
+ test_prelude();
let root = Array::new(vec![1i64, 2]);
let seen = RefCell::new(Vec::new());
assert!(structural_visit(
@@ -1842,6 +1885,7 @@ fn callback_visit_with_overrides_child_def_region() {
#[test]
fn nested_callback_visit_restores_the_outer_active_visitor() {
+ test_prelude();
let outer = Array::new(vec![10i64, 20]);
let inner = Array::new(vec![1i64, 2]);
let entered_inner = Cell::new(false);
@@ -1870,17 +1914,26 @@ fn
nested_callback_visit_restores_the_outer_active_visitor() {
#[test]
fn callback_visit_panics_resume_and_leave_the_next_run_usable() {
+ test_prelude();
let root = Array::new(vec![1i64]);
- let panic = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(||
{
+ let entered_callback = Cell::new(false);
+ let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
structural_visit(
&root,
|_value: i64, _visitor: &mut VisitContext<'_, ()>| -> () {
+ entered_callback.set(true);
panic!("callback visitor panic")
},
)
- })) {
+ }));
+ assert!(
+ entered_callback.get(),
+ "panicking callback visitor was never called"
+ );
+ let panic = match outcome {
Err(panic) => panic,
- Ok(_) => panic!("panicking callback visitor unexpectedly returned"),
+ Ok(Err(error)) => panic!("panicking callback visitor returned an
error: {error}"),
+ Ok(Ok(_)) => panic!("panicking callback visitor unexpectedly
returned"),
};
assert_eq!(
panic.downcast_ref::<&str>().copied(),