@@ -360,6 +360,26 @@ impl RoutingFrame {
360
360
///
361
361
/// ---
362
362
///
363
+ /// ### Canonical Handling of Zero-Dimensional Slices
364
+ ///
365
+ /// A `Slice` with zero dimensions represents the empty product
366
+ /// `∏_{i=1}^{0} Xᵢ`, which has exactly one element: the empty
367
+ /// tuple. To maintain uniform routing semantics, we canonically
368
+ /// embed such 0D slices as 1D slices of extent 1:
369
+ ///
370
+ /// ```text
371
+ /// Slice::new(offset, [1], [1])
372
+ /// ```
373
+ ///
374
+ /// This embedding preserves the correct number of addressable
375
+ /// points and allows the routing machinery to proceed through the
376
+ /// usual recursive strategy without introducing special cases. The
377
+ /// selected coordinate is `vec![0]`, and `dim = 0` proceeds as
378
+ /// usual. This makes the routing logic consistent with evaluation
379
+ /// and avoids edge case handling throughout the codebase.
380
+ ///
381
+ /// ---
382
+ ///
363
383
/// ### Summary
364
384
///
365
385
/// - **Structure-driven**: Mirrors the shape of the selection
@@ -378,6 +398,14 @@ impl RoutingFrame {
378
398
_chooser : & mut dyn FnMut ( & Choice ) -> usize ,
379
399
f : & mut dyn FnMut ( RoutingStep ) -> ControlFlow < ( ) > ,
380
400
) -> ControlFlow < ( ) > {
401
+ if self . slice . num_dim ( ) == 0 {
402
+ // Canonically embed 0D as 1D (extent 1).
403
+ let embedded = Slice :: new ( self . slice . offset ( ) , vec ! [ 1 ] , vec ! [ 1 ] ) . unwrap ( ) ;
404
+ let mut this = self . clone ( ) ;
405
+ this. slice = Arc :: new ( embedded) ;
406
+ this. here = vec ! [ 0 ] ;
407
+ return this. next_steps ( _chooser, f) ;
408
+ }
381
409
let selection = self
382
410
. selection
383
411
. clone ( )
@@ -1590,4 +1618,48 @@ mod tests {
1590
1618
"Expected panic due to overdelivery, but no panic occurred"
1591
1619
) ;
1592
1620
}
1621
+
1622
+ #[ test]
1623
+ fn test_next_steps_zero_dim_slice ( ) {
1624
+ use std:: ops:: ControlFlow ;
1625
+
1626
+ use crate :: selection:: dsl:: * ;
1627
+
1628
+ let slice = Slice :: new ( 42 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
1629
+ let selection = true_ ( ) ;
1630
+ let frame = RoutingFrame :: root ( selection, slice. clone ( ) ) ;
1631
+
1632
+ let mut steps = vec ! [ ] ;
1633
+ let _ = frame. next_steps (
1634
+ & mut |_| panic ! ( "Unexpected Choice in 0D test" ) ,
1635
+ & mut |step| {
1636
+ steps. push ( step) ;
1637
+ ControlFlow :: Continue ( ( ) )
1638
+ } ,
1639
+ ) ;
1640
+
1641
+ assert_eq ! ( steps. len( ) , 1 ) ;
1642
+ let step = steps[ 0 ] . as_forward ( ) . unwrap ( ) ;
1643
+ assert_eq ! ( step. here, vec![ 0 ] ) ;
1644
+ assert ! ( step. deliver_here( ) ) ;
1645
+ assert_eq ! ( step. slice. location( & step. here) . unwrap( ) , 42 ) ;
1646
+
1647
+ let selection = all ( false_ ( ) ) ;
1648
+ let frame = RoutingFrame :: root ( selection, slice) ;
1649
+
1650
+ let mut steps = vec ! [ ] ;
1651
+ let _ = frame. next_steps (
1652
+ & mut |_| panic ! ( "Unexpected Choice in 0D test" ) ,
1653
+ & mut |step| {
1654
+ steps. push ( step) ;
1655
+ ControlFlow :: Continue ( ( ) )
1656
+ } ,
1657
+ ) ;
1658
+
1659
+ assert_eq ! ( steps. len( ) , 1 ) ;
1660
+ let step = steps[ 0 ] . as_forward ( ) . unwrap ( ) ;
1661
+ assert_eq ! ( step. here, vec![ 0 ] ) ;
1662
+ assert ! ( !step. deliver_here( ) ) ;
1663
+ assert_eq ! ( step. slice. location( & step. here) . unwrap( ) , 42 ) ;
1664
+ }
1593
1665
}
0 commit comments