@@ -24,6 +24,64 @@ impl AstReconstructor for DestructuringVisitor<'_> {
2424 type AdditionalInput = ( ) ;
2525 type AdditionalOutput = Vec < Statement > ;
2626
27+ /// Reconstructs a binary expression, expanding equality and inequality over
28+ /// tuples into elementwise comparisons. When both sides are tuples and the
29+ /// operator is `==` or `!=`, it generates per-element comparisons and folds
30+ /// them with AND/OR; otherwise the expression is rebuilt normally.
31+ ///
32+ /// Example: `(a, b) == (c, d)` → `(a == c) && (b == d)`
33+ /// Example: `(a, b, c) != (x, y, z)` → `(a != x) || (b != y) || (c != z)`
34+ fn reconstruct_binary (
35+ & mut self ,
36+ input : BinaryExpression ,
37+ _additional : & Self :: AdditionalInput ,
38+ ) -> ( Expression , Self :: AdditionalOutput ) {
39+ let ( left, mut statements) = self . reconstruct_expression_tuple ( input. left ) ;
40+ let ( right, statements2) = self . reconstruct_expression_tuple ( input. right ) ;
41+ statements. extend ( statements2) ;
42+
43+ use BinaryOperation :: * ;
44+
45+ // Tuple equality / inequality expansion
46+ if let ( Expression :: Tuple ( tuple_left) , Expression :: Tuple ( tuple_right) ) = ( & left, & right)
47+ && matches ! ( input. op, Eq | Neq )
48+ {
49+ assert_eq ! ( tuple_left. elements. len( ) , tuple_right. elements. len( ) ) ;
50+
51+ // Directly build elementwise (l OP r)
52+ let pieces: Vec < Expression > = tuple_left
53+ . elements
54+ . iter ( )
55+ . zip ( & tuple_right. elements )
56+ . map ( |( l, r) | {
57+ let expr: Expression = BinaryExpression {
58+ op : input. op ,
59+ left : l. clone ( ) ,
60+ right : r. clone ( ) ,
61+ span : Default :: default ( ) ,
62+ id : self . state . node_builder . next_id ( ) ,
63+ }
64+ . into ( ) ;
65+
66+ self . state . type_table . insert ( expr. id ( ) , Type :: Boolean ) ;
67+ expr
68+ } )
69+ . collect ( ) ;
70+
71+ // Fold appropriately
72+ let combined = match input. op {
73+ Eq => self . fold_with_and ( pieces) ,
74+ Neq => self . fold_with_or ( pieces) ,
75+ _ => unreachable ! ( ) ,
76+ } ;
77+
78+ return ( combined, statements) ;
79+ }
80+
81+ // Fallback
82+ ( BinaryExpression { op : input. op , left, right, ..input } . into ( ) , Default :: default ( ) )
83+ }
84+
2785 /// Replaces a tuple access expression with the appropriate expression.
2886 fn reconstruct_tuple_access (
2987 & mut self ,
@@ -66,9 +124,12 @@ impl AstReconstructor for DestructuringVisitor<'_> {
66124 mut input : TernaryExpression ,
67125 _additional : & ( ) ,
68126 ) -> ( Expression , Self :: AdditionalOutput ) {
69- let ( if_true, mut statements) = self . reconstruct_expression_tuple ( std:: mem:: take ( & mut input. if_true ) ) ;
70- let ( if_false, statements2) = self . reconstruct_expression_tuple ( std:: mem:: take ( & mut input. if_false ) ) ;
127+ let ( condition, mut statements) =
128+ self . reconstruct_expression ( std:: mem:: take ( & mut input. condition ) , & Default :: default ( ) ) ;
129+ let ( if_true, statements2) = self . reconstruct_expression_tuple ( std:: mem:: take ( & mut input. if_true ) ) ;
71130 statements. extend ( statements2) ;
131+ let ( if_false, statements3) = self . reconstruct_expression_tuple ( std:: mem:: take ( & mut input. if_false ) ) ;
132+ statements. extend ( statements3) ;
72133
73134 match ( if_true, if_false) {
74135 ( Expression :: Tuple ( tuple_true) , Expression :: Tuple ( tuple_false) ) => {
@@ -77,20 +138,17 @@ impl AstReconstructor for DestructuringVisitor<'_> {
77138 panic ! ( "Should have tuple type" ) ;
78139 } ;
79140
80- // We'll be reusing `input. condition`, so assign it to a variable.
81- let cond = if let Expression :: Path ( ..) = input . condition {
82- input . condition
141+ // We'll be reusing `condition`, so assign it to a variable.
142+ let cond = if let Expression :: Path ( ..) = condition {
143+ condition
83144 } else {
84145 let place = Identifier :: new (
85146 self . state . assigner . unique_symbol ( "cond" , "$$" ) ,
86147 self . state . node_builder . next_id ( ) ,
87148 ) ;
88149
89- let definition = self . state . assigner . simple_definition (
90- place,
91- input. condition ,
92- self . state . node_builder . next_id ( ) ,
93- ) ;
150+ let definition =
151+ self . state . assigner . simple_definition ( place, condition, self . state . node_builder . next_id ( ) ) ;
94152
95153 statements. push ( definition) ;
96154
@@ -144,12 +202,59 @@ impl AstReconstructor for DestructuringVisitor<'_> {
144202 }
145203 ( if_true, if_false) => {
146204 // This isn't a tuple. Just rebuild it and otherwise leave it alone.
147- ( TernaryExpression { if_true, if_false, ..input } . into ( ) , statements)
205+ ( TernaryExpression { condition , if_true, if_false, ..input } . into ( ) , statements)
148206 }
149207 }
150208 }
151209
152210 /* Statements */
211+ /// `assert_eq` and `assert_neq` comparing tuples should be expanded to as many asserts as
212+ /// the length of each tuple.
213+ fn reconstruct_assert ( & mut self , input : AssertStatement ) -> ( Statement , Self :: AdditionalOutput ) {
214+ match input. variant {
215+ AssertVariant :: Assert ( expr) => {
216+ // Simple assert, just reconstruct the expression.
217+ let ( expr, _) = self . reconstruct_expression ( expr, & Default :: default ( ) ) ;
218+ ( AssertStatement { variant : AssertVariant :: Assert ( expr) , ..input } . into ( ) , Default :: default ( ) )
219+ }
220+ AssertVariant :: AssertEq ( ref left, ref right) | AssertVariant :: AssertNeq ( ref left, ref right) => {
221+ let ( left, mut statements) = self . reconstruct_expression_tuple ( left. clone ( ) ) ;
222+ let ( right, statements2) = self . reconstruct_expression_tuple ( right. clone ( ) ) ;
223+ statements. extend ( statements2) ;
224+
225+ match ( & left, & right) {
226+ ( Expression :: Tuple ( tuple_left) , Expression :: Tuple ( tuple_right) ) => {
227+ // Ensure the tuple lengths match
228+ assert_eq ! ( tuple_left. elements. len( ) , tuple_right. elements. len( ) ) ;
229+
230+ for ( l, r) in tuple_left. elements . iter ( ) . zip ( & tuple_right. elements ) {
231+ let assert_variant = match input. variant {
232+ AssertVariant :: AssertEq ( _, _) => AssertVariant :: AssertEq ( l. clone ( ) , r. clone ( ) ) ,
233+ AssertVariant :: AssertNeq ( _, _) => AssertVariant :: AssertNeq ( l. clone ( ) , r. clone ( ) ) ,
234+ _ => unreachable ! ( ) ,
235+ } ;
236+
237+ let stmt = AssertStatement { variant : assert_variant, ..input. clone ( ) } . into ( ) ;
238+ statements. push ( stmt) ;
239+ }
240+
241+ // We don't need the original statement, just the ones we've created.
242+ ( Statement :: dummy ( ) , statements)
243+ }
244+ _ => {
245+ // Not tuples, just keep the original assert
246+ let variant = match input. variant {
247+ AssertVariant :: AssertEq ( _, _) => AssertVariant :: AssertEq ( left, right) ,
248+ AssertVariant :: AssertNeq ( _, _) => AssertVariant :: AssertNeq ( left, right) ,
249+ _ => unreachable ! ( ) ,
250+ } ;
251+ ( AssertStatement { variant, ..input } . into ( ) , Default :: default ( ) )
252+ }
253+ }
254+ }
255+ }
256+ }
257+
153258 /// Modify assignments to tuples to become assignments to the corresponding variables.
154259 ///
155260 /// There are two cases we handle:
0 commit comments