@@ -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,16 +30,17 @@ 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 ctx = UnsafePanicContext { db, libfuncs_with_sideffect } ;
34+ let analysis = DataflowBackAnalysis :: new ( lowered, ctx) ;
35+ let mut result = analysis. run ( ) ;
36+
37+ // If the entry point itself is unreachable, add a fix for it.
38+ if let Reachability :: Unreachable ( location) = result. reachability {
39+ result. fixes . push ( ( ( BlockId :: root ( ) , 0 ) , location) ) ;
40+ }
4241
4342 let panic_func_id = core. submodule ( "panics" ) . function_id ( "unsafe_panic" , vec ! [ ] ) . lowered ( db) ;
44- for ( ( block_id, statement_idx) , location) in fixes {
43+ for ( ( block_id, statement_idx) , location) in result . fixes {
4544 let block = & mut lowered. blocks [ block_id] ;
4645 block. statements . truncate ( statement_idx) ;
4746
@@ -59,9 +58,6 @@ pub fn early_unsafe_panic<'db>(db: &'db dyn Database, lowered: &mut Lowered<'db>
5958pub struct UnsafePanicContext < ' db > {
6059 db : & ' db dyn Database ,
6160
62- /// The list of blocks where we can insert unsafe_panic.
63- fixes : Vec < ( StatementLocation , LocationId < ' db > ) > ,
64-
6561 /// libfuncs with side effects that we need to ignore.
6662 libfuncs_with_sideffect : HashSet < ExternFunctionId < ' db > > ,
6763}
@@ -83,58 +79,104 @@ impl<'db> UnsafePanicContext<'db> {
8379 }
8480}
8581
86- /// Can this state lead to a return or a statement with side effect .
87- #[ derive( Clone , Default , PartialEq , Debug ) ]
88- pub enum ReachableSideEffects < ' db > {
82+ /// Reachability state for a point in the program .
83+ #[ derive( Clone , Copy , Default , PartialEq , Debug ) ]
84+ pub enum Reachability < ' db > {
8985 /// Some return statement or statement with side effect is reachable.
9086 #[ default]
9187 Reachable ,
9288 /// No return statement or statement with side effect is reachable.
93- /// holds the location of the closest match with no returning arms.
89+ /// Holds the location of the closest match with no returning arms.
9490 Unreachable ( LocationId < ' db > ) ,
9591}
9692
97- impl < ' db > Analyzer < ' db , ' _ > for UnsafePanicContext < ' db > {
98- type Info = ReachableSideEffects < ' db > ;
93+ /// Analysis info containing reachability state and accumulated fixes.
94+ #[ derive( Clone , Default , Debug ) ]
95+ pub struct AnalysisInfo < ' db > {
96+ /// The reachability state at this program point.
97+ pub reachability : Reachability < ' db > ,
98+ /// Locations where we need to insert unsafe_panic.
99+ pub fixes : Vec < ( StatementLocation , LocationId < ' db > ) > ,
100+ }
101+
102+ impl < ' db , ' a > DataflowAnalyzer < ' db , ' a > for UnsafePanicContext < ' db > {
103+ type Info = AnalysisInfo < ' db > ;
104+ const DIRECTION : Direction = Direction :: Backward ;
105+
106+ fn initial_info ( & mut self , _block_id : BlockId , _block_end : & ' a BlockEnd < ' db > ) -> Self :: Info {
107+ AnalysisInfo :: default ( )
108+ }
99109
100- fn visit_stmt (
110+ fn merge (
101111 & mut self ,
102- info : & mut Self :: Info ,
103- statement_location : StatementLocation ,
104- stmt : & Statement < ' db > ,
105- ) {
106- if self . has_side_effects ( stmt)
107- && let ReachableSideEffects :: Unreachable ( locations) = * info
108- {
109- self . fixes . push ( ( statement_location, locations) ) ;
110- * info = ReachableSideEffects :: Reachable
112+ _statement_location : StatementLocation ,
113+ infos : impl Iterator < Item = ( BlockId , Self :: Info ) > ,
114+ ) -> Self :: Info {
115+ let mut result = AnalysisInfo :: default ( ) ;
116+ let mut all_unreachable = true ;
117+ let mut unreachable_location = None ;
118+
119+ for ( src, info) in infos {
120+ result. fixes . extend ( info. fixes ) ;
121+ if let Reachability :: Unreachable ( loc) = info. reachability {
122+ // Fix at the entry of this unreachable branch.
123+ result. fixes . push ( ( ( src, 0 ) , loc) ) ;
124+ unreachable_location. get_or_insert ( loc) ;
125+ } else {
126+ all_unreachable = false ;
127+ }
128+ }
129+
130+ if all_unreachable && let Some ( loc) = unreachable_location {
131+ result. reachability = Reachability :: Unreachable ( loc) ;
111132 }
133+
134+ result
112135 }
113136
114137 fn merge_match (
115138 & mut self ,
116139 statement_location : StatementLocation ,
117- match_info : & MatchInfo < ' db > ,
140+ match_info : & ' a MatchInfo < ' db > ,
118141 infos : impl Iterator < Item = Self :: Info > ,
119142 ) -> 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 {
123- ReachableSideEffects :: Reachable => {
124- res = ReachableSideEffects :: Reachable ;
143+ let mut result = AnalysisInfo :: default ( ) ;
144+ let mut all_unreachable = true ;
145+
146+ for ( arm, info) in match_info. arms ( ) . iter ( ) . zip ( infos) {
147+ result. fixes . extend ( info. fixes ) ;
148+ match info. reachability {
149+ Reachability :: Reachable => {
150+ all_unreachable = false ;
151+ }
152+ Reachability :: Unreachable ( loc) => {
153+ // Fix at the entry of this unreachable arm.
154+ result. fixes . push ( ( ( arm. block_id , 0 ) , loc) ) ;
125155 }
126- ReachableSideEffects :: Unreachable ( l) => self . fixes . push ( ( ( arm. block_id , 0 ) , l) ) ,
127156 }
128157 }
129158
130- if let ReachableSideEffects :: Unreachable ( location) = res {
131- self . fixes . push ( ( statement_location, location) ) ;
159+ if all_unreachable {
160+ let loc = * match_info. location ( ) ;
161+ result. reachability = Reachability :: Unreachable ( loc) ;
162+ // Fix at the match statement itself.
163+ result. fixes . push ( ( statement_location, loc) ) ;
132164 }
133165
134- res
166+ result
135167 }
136168
137- fn info_from_return ( & mut self , _: StatementLocation , _vars : & [ VarUsage < ' db > ] ) -> Self :: Info {
138- ReachableSideEffects :: Reachable
169+ fn transfer_stmt (
170+ & mut self ,
171+ info : & mut Self :: Info ,
172+ statement_location : StatementLocation ,
173+ stmt : & ' a Statement < ' db > ,
174+ ) {
175+ if self . has_side_effects ( stmt)
176+ && let Reachability :: Unreachable ( loc) = info. reachability
177+ {
178+ info. fixes . push ( ( statement_location, loc) ) ;
179+ info. reachability = Reachability :: Reachable ;
180+ }
139181 }
140182}
0 commit comments