@@ -12,6 +12,7 @@ use cairo_lang_semantic::types::{TypesSemantic, peel_snapshots, wrap_in_snapshot
1212use cairo_lang_semantic:: { ConcreteTypeId , GenericArgumentId , TypeId , TypeLongId } ;
1313use cairo_lang_utils:: ordered_hash_map:: { Entry , OrderedHashMap } ;
1414use cairo_lang_utils:: ordered_hash_set:: OrderedHashSet ;
15+ use itertools:: Itertools ;
1516use salsa:: Database ;
1617
1718use crate :: borrow_check:: analysis:: StatementLocation ;
@@ -183,7 +184,10 @@ pub fn apply_reboxing_candidates<'db>(
183184
184185 trace ! ( "Applying {} reboxing optimization(s)." , candidates. len( ) ) ;
185186
186- for candidate in candidates {
187+ // We sort the candidates such that removal of statements can be done in reverse order.
188+ for candidate in
189+ candidates. iter ( ) . sorted_by_key ( |candidate| candidate. into_box_location . 1 ) . rev ( )
190+ {
187191 apply_reboxing_candidate ( db, lowered, candidate) ;
188192 }
189193}
@@ -215,13 +219,29 @@ fn apply_reboxing_candidate<'db>(
215219 candidate. reboxed_var. index( )
216220 ) ;
217221
218- // TODO(eytan-starkware): Handle snapshot of box (e.g., @Box<T>).
219- // Only support MemberOfUnboxed where source is Unboxed for now.
220- let ReboxingValue :: MemberOfUnboxed { source, member } = & candidate. source else {
221- // If source is not member of unboxed, we are reboxing original value which is not supported
222- // yet.
223- return ;
222+ match & candidate. source {
223+ ReboxingValue :: Revoked => ( ) ,
224+ ReboxingValue :: Unboxed ( id) => {
225+ replace_variable_usages ( lowered, candidate. reboxed_var , * id) ;
226+ // Remove the statement that unboxes the struct.
227+ lowered. blocks [ candidate. into_box_location . 0 ]
228+ . statements
229+ . remove ( candidate. into_box_location . 1 ) ;
230+ }
231+ ReboxingValue :: MemberOfUnboxed { source, member } => {
232+ replace_into_box_call ( db, lowered, candidate, source, * member)
233+ }
224234 } ;
235+ }
236+
237+ /// Replaces the call to `into_box` with a call to `struct_boxed_deconstruct`.
238+ fn replace_into_box_call < ' db > (
239+ db : & ' db dyn Database ,
240+ lowered : & mut Lowered < ' db > ,
241+ candidate : & ReboxCandidate ,
242+ source : & Rc < ReboxingValue > ,
243+ member : usize ,
244+ ) {
225245 let ReboxingValue :: Unboxed ( source_var) = * * source else {
226246 // When source of the value is not `Unboxes`, it is a nested MemberOfUnboxed, which is not
227247 // supported yet.
@@ -232,7 +252,7 @@ fn apply_reboxing_candidate<'db>(
232252 db,
233253 & mut lowered. variables ,
234254 source_var,
235- * member,
255+ member,
236256 candidate. reboxed_var ,
237257 & lowered. blocks [ candidate. into_box_location . 0 ] . statements [ candidate. into_box_location . 1 ] ,
238258 ) {
@@ -244,6 +264,59 @@ fn apply_reboxing_candidate<'db>(
244264 }
245265}
246266
267+ /// Replaces all usages of old_var with new_var throughout the lowered function.
268+ fn replace_variable_usages ( lowered : & mut Lowered < ' _ > , old_var : VariableId , new_var : VariableId ) {
269+ if old_var == new_var {
270+ return ;
271+ }
272+
273+ for block in lowered. blocks . iter_mut ( ) {
274+ for stmt in & mut block. statements {
275+ for input in stmt. inputs_mut ( ) {
276+ if input. var_id == old_var {
277+ input. var_id = new_var;
278+ }
279+ }
280+ }
281+
282+ match & mut block. end {
283+ BlockEnd :: Return ( var_usages, _) => {
284+ for return_var in var_usages. iter_mut ( ) {
285+ if return_var. var_id == old_var {
286+ return_var. var_id = new_var;
287+ }
288+ }
289+ }
290+ BlockEnd :: Goto ( _, remapping) => {
291+ // Collect all changes needed
292+ let mut changes = Vec :: new ( ) ;
293+ for ( dst, src_usage) in remapping. iter ( ) {
294+ if * dst == old_var {
295+ changes. push ( ( * dst, Some ( new_var) , * src_usage) ) ;
296+ } else if src_usage. var_id == old_var {
297+ let mut new_usage = * src_usage;
298+ new_usage. var_id = new_var;
299+ changes. push ( ( * dst, None , new_usage) ) ;
300+ }
301+ }
302+
303+ // Apply changes
304+ for ( old_key, new_key_opt, new_usage) in changes {
305+ if let Some ( new_key) = new_key_opt {
306+ // Key needs to change
307+ remapping. swap_remove ( & old_key) ;
308+ remapping. insert ( new_key, new_usage) ;
309+ } else {
310+ // Only value needs to change
311+ remapping. insert ( old_key, new_usage) ;
312+ }
313+ }
314+ }
315+ _ => { }
316+ }
317+ }
318+ }
319+
247320/// Creates a struct_boxed_deconstruct call statement.
248321/// Returns None if the call cannot be created.
249322fn create_struct_boxed_deconstruct_call < ' db > (
0 commit comments