@@ -56,7 +56,7 @@ pub(super) enum Branches {
5656 /// No use in this branch or there is no branch
5757 None ,
5858 Direct ( IdentId ) ,
59- IfOrMatch ( Vec < Branches > ) ,
59+ IfOrMatch ( IfOrMatchId , HashMap < BranchId , Branches > ) ,
6060}
6161
6262impl Branches {
@@ -65,28 +65,49 @@ impl Branches {
6565 match self {
6666 Branches :: None => Vec :: new ( ) ,
6767 Branches :: Direct ( ident_id) => vec ! [ ident_id] ,
68- Branches :: IfOrMatch ( cases) => cases. into_iter ( ) . flat_map ( Self :: flatten_uses) . collect ( ) ,
68+ Branches :: IfOrMatch ( _, cases) => {
69+ cases. into_values ( ) . flat_map ( Self :: flatten_uses) . collect ( )
70+ }
71+ }
72+ }
73+
74+ fn get_if_or_match_id ( & self ) -> Option < IfOrMatchId > {
75+ match self {
76+ Branches :: IfOrMatch ( id, _) => Some ( * id) ,
77+ _ => None ,
78+ }
79+ }
80+
81+ fn get_branches_map ( & mut self ) -> Option < & mut HashMap < BranchId , Branches > > {
82+ match self {
83+ Branches :: IfOrMatch ( _, map) => Some ( map) ,
84+ _ => None ,
6985 }
7086 }
7187}
7288
7389/// A single path through a `Branches` enum.
7490///
7591/// This is used by the context to keep track of where we currently are within a function.
76- #[ derive( Debug ) ]
77- enum BranchesPath {
78- /// We've reached our destination
79- Here ,
80- /// We're in a fork in the road, take the branch at the given index
81- IfOrMatch { branch_index : usize , rest : Box < BranchesPath > } ,
82- }
92+ type BranchesPath = Vec < ( IfOrMatchId , BranchId ) > ;
93+
94+ /// The Id of an `if` or `match`, used to distinguish multiple sequential ifs/matches
95+ /// from each other.
96+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
97+ pub ( super ) struct IfOrMatchId ( u32 ) ;
98+
99+ /// The Id for a single branch of an if or match
100+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
101+ pub ( super ) struct BranchId ( u32 ) ;
83102
84103struct LastUseContext {
85104 /// The outer `Vec` is each loop we're currently in, while the `BranchPath` contains
86105 /// the path to overwrite the last use in any `Branches` enums of the variables we find.
87106 /// As a whole, this tracks the current control-flow of the function we're in.
88107 current_loop_and_branch : Vec < BranchesPath > ,
89108
109+ next_id : u32 ,
110+
90111 /// Stores the location of each variable's last use
91112 ///
92113 /// Map from each local variable to the last instance of that variable. Separate uses of
@@ -132,8 +153,12 @@ impl Context {
132153 pub ( super ) fn find_last_uses_of_variables (
133154 function : & Function ,
134155 ) -> HashMap < LocalId , Vec < IdentId > > {
135- let mut context =
136- LastUseContext { current_loop_and_branch : Vec :: new ( ) , last_uses : HashMap :: default ( ) } ;
156+ let mut context = LastUseContext {
157+ current_loop_and_branch : Vec :: new ( ) ,
158+ last_uses : HashMap :: default ( ) ,
159+ next_id : 0 ,
160+ } ;
161+
137162 context. push_loop_scope ( ) ;
138163 for ( parameter, ..) in & function. parameters {
139164 context. declare_variable ( * parameter) ;
@@ -145,29 +170,30 @@ impl Context {
145170
146171impl LastUseContext {
147172 fn push_loop_scope ( & mut self ) {
148- self . current_loop_and_branch . push ( BranchesPath :: Here ) ;
173+ self . current_loop_and_branch . push ( BranchesPath :: new ( ) ) ;
149174 }
150175
151176 fn pop_loop_scope ( & mut self ) {
152- self . current_loop_and_branch . pop ( ) ;
177+ self . current_loop_and_branch . pop ( ) . expect ( "No loop to pop" ) ;
153178 }
154179
155- fn branch ( & mut self , branch_index : usize ) {
180+ fn branch ( & mut self , id : IfOrMatchId , branch_id : u32 ) {
156181 let path =
157182 self . current_loop_and_branch . last_mut ( ) . expect ( "We should always have at least 1 path" ) ;
158- let rest = Box :: new ( std:: mem:: replace ( path, BranchesPath :: Here ) ) ;
159- * path = BranchesPath :: IfOrMatch { branch_index, rest } ;
183+
184+ path. push ( ( id, BranchId ( branch_id) ) ) ;
185+ }
186+
187+ fn next_if_or_match_id ( & mut self ) -> IfOrMatchId {
188+ let id = self . next_id ;
189+ self . next_id += 1 ;
190+ IfOrMatchId ( id)
160191 }
161192
162193 fn unbranch ( & mut self ) {
163194 let path =
164195 self . current_loop_and_branch . last_mut ( ) . expect ( "We should always have at least 1 path" ) ;
165- let rest = std:: mem:: replace ( path, BranchesPath :: Here ) ;
166-
167- match rest {
168- BranchesPath :: Here => panic ! ( "unbranch called without any branches" ) ,
169- BranchesPath :: IfOrMatch { branch_index : _, rest } => * path = * rest,
170- }
196+ path. pop ( ) . expect ( "No branch to unbranch" ) ;
171197 }
172198
173199 fn loop_index ( & self ) -> usize {
@@ -203,37 +229,38 @@ impl LastUseContext {
203229
204230 fn remember_use_of_variable_rec (
205231 branches : & mut Branches ,
206- path : & BranchesPath ,
232+ path : & [ ( IfOrMatchId , BranchId ) ] ,
207233 variable : IdentId ,
208234 ) {
235+ let reset_branch_and_recur = |branch : & mut Branches , if_or_match_id, branch_id| {
236+ let empty_branch = [ ( branch_id, Branches :: None ) ] . into_iter ( ) . collect ( ) ;
237+ * branch = Branches :: IfOrMatch ( if_or_match_id, empty_branch) ;
238+ Self :: remember_use_of_variable_rec ( branch, path, variable) ;
239+ } ;
240+
209241 match ( branches, path) {
210242 // Path is direct, overwrite the last use entirely
211- ( branch, BranchesPath :: Here ) => {
243+ ( branch, [ ] ) => {
212244 * branch = Branches :: Direct ( variable) ;
213245 }
214246 // Our path says we need to enter an if or match but the variable's last
215247 // use was direct. So we need to overwrite the last use with an empty IfOrMatch
216248 // and write to the relevant branch of that
217249 (
218250 branch @ ( Branches :: None | Branches :: Direct { .. } ) ,
219- BranchesPath :: IfOrMatch { branch_index , rest : _ } ,
251+ [ ( if_or_match_id , branch_id ) , .. ] ,
220252 ) => {
221253 // The branch doesn't exist for this variable; create it
222- let empty_branches =
223- std:: iter:: repeat_n ( Branches :: None , * branch_index + 1 ) . collect ( ) ;
224- * branch = Branches :: IfOrMatch ( empty_branches) ;
225- Self :: remember_use_of_variable_rec ( branch, path, variable) ;
254+ reset_branch_and_recur ( branch, * if_or_match_id, * branch_id) ;
226255 }
227- // The variable's last use was in an if or match, and our current path is in one
228- // as well so just follow to the relevant branch in both. We just need to possibly
229- // resize the variable's branches count in case it was created with fewer than we
230- // know we currently have.
231- ( Branches :: IfOrMatch ( branches) , BranchesPath :: IfOrMatch { branch_index, rest } ) => {
232- let required_len = * branch_index + 1 ;
233- if branches. len ( ) < required_len {
234- branches. resize ( required_len, Branches :: None ) ;
256+ ( branches @ Branches :: IfOrMatch ( ..) , [ ( new_if_id, branch_id) , rest @ ..] ) => {
257+ if branches. get_if_or_match_id ( ) == Some ( * new_if_id) {
258+ let nested = branches. get_branches_map ( ) . expect ( "We know this is a IfOrMatch" ) ;
259+ let entry = nested. entry ( * branch_id) . or_insert ( Branches :: None ) ;
260+ Self :: remember_use_of_variable_rec ( entry, rest, variable) ;
261+ } else {
262+ reset_branch_and_recur ( branches, * new_if_id, * branch_id) ;
235263 }
236- Self :: remember_use_of_variable_rec ( & mut branches[ * branch_index] , rest, variable) ;
237264 }
238265 }
239266 }
@@ -338,30 +365,33 @@ impl LastUseContext {
338365 fn track_variables_in_if ( & mut self , if_expr : & ast:: If ) {
339366 self . track_variables_in_expression ( & if_expr. condition ) ;
340367
341- self . branch ( 0 ) ;
368+ let if_id = self . next_if_or_match_id ( ) ;
369+ self . branch ( if_id, 0 ) ;
342370 self . track_variables_in_expression ( & if_expr. consequence ) ;
343371 self . unbranch ( ) ;
344372
345373 if let Some ( alt) = & if_expr. alternative {
346- self . branch ( 1 ) ;
374+ self . branch ( if_id , 1 ) ;
347375 self . track_variables_in_expression ( alt) ;
348376 self . unbranch ( ) ;
349377 }
350378 }
351379
352380 fn track_variables_in_match ( & mut self , match_expr : & ast:: Match ) {
381+ let match_id = self . next_if_or_match_id ( ) ;
382+
353383 for ( i, case) in match_expr. cases . iter ( ) . enumerate ( ) {
354384 for argument in & case. arguments {
355385 self . declare_variable ( * argument) ;
356386 }
357387
358- self . branch ( i ) ;
388+ self . branch ( match_id , i as u32 ) ;
359389 self . track_variables_in_expression ( & case. branch ) ;
360390 self . unbranch ( ) ;
361391 }
362392
363393 if let Some ( default_case) = & match_expr. default_case {
364- self . branch ( match_expr. cases . len ( ) ) ;
394+ self . branch ( match_id , match_expr. cases . len ( ) as u32 ) ;
365395 self . track_variables_in_expression ( default_case) ;
366396 self . unbranch ( ) ;
367397 }
0 commit comments