@@ -7,14 +7,12 @@ use std::collections::HashSet;
77use cairo_lang_defs:: ids:: ExternFunctionId ;
88use cairo_lang_filesystem:: flag:: FlagsGroup ;
99use cairo_lang_semantic:: helper:: ModuleHelper ;
10- use itertools:: zip_eq;
1110use salsa:: Database ;
1211
13- use crate :: analysis:: { Analyzer , BackAnalysis , StatementLocation } ;
12+ use crate :: analysis:: core:: StatementLocation ;
13+ use crate :: analysis:: { DataflowAnalyzer , DataflowBackAnalysis , Direction } ;
1414use crate :: ids:: { LocationId , SemanticFunctionIdEx } ;
15- use crate :: {
16- BlockEnd , BlockId , Lowered , MatchExternInfo , MatchInfo , Statement , StatementCall , VarUsage ,
17- } ;
15+ use crate :: { BlockEnd , BlockId , Lowered , MatchExternInfo , MatchInfo , Statement , StatementCall } ;
1816
1917/// Adds an early unsafe_panic when we detect that `return` is unreachable from a certain point in
2018/// the code. This step is needed to avoid issues with undroppable references in Sierra to CASM.
@@ -32,13 +30,14 @@ pub fn early_unsafe_panic<'db>(db: &'db dyn Database, lowered: &mut Lowered<'db>
3230 core. submodule ( "internal" ) . extern_function_id ( "trace" ) ,
3331 ] ) ;
3432
35- let ctx = UnsafePanicContext { db, fixes : vec ! [ ] , libfuncs_with_sideffect } ;
36- let mut analysis = BackAnalysis :: new ( lowered, ctx) ;
37- let fixes = if let ReachableSideEffects :: Unreachable ( location) = analysis. get_root_info ( ) {
38- vec ! [ ( ( BlockId :: root( ) , 0 ) , location) ]
39- } else {
40- analysis. analyzer . fixes
41- } ;
33+ let mut ctx = UnsafePanicContext { db, libfuncs_with_sideffect, fixes : Vec :: new ( ) } ;
34+ let result = DataflowBackAnalysis :: new ( lowered, & mut ctx) . run ( ) ;
35+ let UnsafePanicContext { mut fixes, .. } = ctx;
36+
37+ // If the entry point itself is unreachable, add a fix for it.
38+ if let ReachableSideEffects :: Unreachable ( location) = result {
39+ fixes. push ( ( ( BlockId :: root ( ) , 0 ) , location) ) ;
40+ }
4241
4342 let panic_func_id = core. submodule ( "panics" ) . function_id ( "unsafe_panic" , vec ! [ ] ) . lowered ( db) ;
4443 for ( ( block_id, statement_idx) , location) in fixes {
@@ -60,7 +59,7 @@ pub struct UnsafePanicContext<'db> {
6059 db : & ' db dyn Database ,
6160
6261 /// The list of blocks where we can insert unsafe_panic.
63- fixes : Vec < ( StatementLocation , LocationId < ' db > ) > ,
62+ pub fixes : Vec < ( StatementLocation , LocationId < ' db > ) > ,
6463
6564 /// libfuncs with side effects that we need to ignore.
6665 libfuncs_with_sideffect : HashSet < ExternFunctionId < ' db > > ,
@@ -84,57 +83,68 @@ impl<'db> UnsafePanicContext<'db> {
8483}
8584
8685/// Can this state lead to a return or a statement with side effect.
87- #[ derive( Clone , Default , PartialEq , Debug ) ]
86+ #[ derive( Clone , Copy , Default , PartialEq , Debug ) ]
8887pub enum ReachableSideEffects < ' db > {
8988 /// Some return statement or statement with side effect is reachable.
9089 #[ default]
9190 Reachable ,
9291 /// No return statement or statement with side effect is reachable.
93- /// holds the location of the closest match with no returning arms.
92+ /// Holds the location of the closest match with no returning arms.
9493 Unreachable ( LocationId < ' db > ) ,
9594}
9695
97- impl < ' db > Analyzer < ' db , ' _ > for UnsafePanicContext < ' db > {
96+ impl < ' db , ' a > DataflowAnalyzer < ' db , ' a > for UnsafePanicContext < ' db > {
9897 type Info = ReachableSideEffects < ' db > ;
98+ const DIRECTION : Direction = Direction :: Backward ;
9999
100- fn visit_stmt (
100+ fn transfer_stmt (
101101 & mut self ,
102102 info : & mut Self :: Info ,
103103 statement_location : StatementLocation ,
104- stmt : & Statement < ' db > ,
104+ stmt : & ' a Statement < ' db > ,
105105 ) {
106106 if self . has_side_effects ( stmt)
107- && let ReachableSideEffects :: Unreachable ( locations ) = * info
107+ && let ReachableSideEffects :: Unreachable ( loc ) = * info
108108 {
109- self . fixes . push ( ( statement_location, locations ) ) ;
110- * info = ReachableSideEffects :: Reachable
109+ self . fixes . push ( ( statement_location, loc ) ) ;
110+ * info = ReachableSideEffects :: Reachable ;
111111 }
112112 }
113113
114- fn merge_match (
114+ fn merge (
115115 & mut self ,
116+ lowered : & Lowered < ' db > ,
116117 statement_location : StatementLocation ,
117- match_info : & MatchInfo < ' db > ,
118- infos : impl Iterator < Item = Self :: Info > ,
118+ infos : impl Iterator < Item = ( BlockId , Self :: Info ) > ,
119119 ) -> Self :: Info {
120- let mut res = ReachableSideEffects :: Unreachable ( * match_info. location ( ) ) ;
121- for ( arm, info) in zip_eq ( match_info. arms ( ) , infos) {
122- match info {
120+ let mut result = ReachableSideEffects :: default ( ) ;
121+ let mut all_unreachable = true ;
122+
123+ for ( src, reachability) in infos {
124+ match reachability {
123125 ReachableSideEffects :: Reachable => {
124- res = ReachableSideEffects :: Reachable ;
126+ all_unreachable = false ;
127+ }
128+ ReachableSideEffects :: Unreachable ( loc) => {
129+ // Fix at the entry of this unreachable branch.
130+ self . fixes . push ( ( ( src, 0 ) , loc) ) ;
125131 }
126- ReachableSideEffects :: Unreachable ( l) => self . fixes . push ( ( ( arm. block_id , 0 ) , l) ) ,
127132 }
128133 }
129134
130- if let ReachableSideEffects :: Unreachable ( location) = res {
135+ // All branches are unreachable (or there are no branches, e.g., match on `never`).
136+ // Use the match location as the source of unreachability.
137+ if all_unreachable {
138+ // In a backward runner location is always available
139+ let location = self . block_entry_location ( lowered, statement_location. 0 ) ;
140+ result = ReachableSideEffects :: Unreachable ( location) ;
131141 self . fixes . push ( ( statement_location, location) ) ;
132142 }
133143
134- res
144+ result
135145 }
136146
137- fn info_from_return ( & mut self , _ : StatementLocation , _vars : & [ VarUsage < ' db > ] ) -> Self :: Info {
138- ReachableSideEffects :: Reachable
147+ fn initial_info ( & mut self , _block_id : BlockId , _block_end : & ' a BlockEnd < ' db > ) -> Self :: Info {
148+ ReachableSideEffects :: default ( )
139149 }
140150}
0 commit comments