@@ -24,25 +24,32 @@ enum ConditionState {
2424#[ derive( Debug , Default ) ]
2525struct ReturnFlow {
2626 return_points : Vec < LuaReturnPoint > ,
27- can_continue : bool ,
27+ can_fall_through : bool ,
2828 can_break : bool ,
29- // `can_stall` means control can get stuck without returning, e.g. `while true do end`.
30- can_stall : bool ,
29+ // Some reachable path is proven to loop forever instead of returning or
30+ // reaching the following statements, e.g. `while true do end`.
31+ is_infinite : bool ,
32+ // Some reachable path can stay inside a runtime-dependent loop without
33+ // proving the body itself is infinite. Keep this separate from
34+ // reachability so stricter loop diagnostics can opt into it later without
35+ // affecting inferred return types.
36+ may_diverge : bool ,
3137}
3238
3339impl ReturnFlow {
34- fn continue_flow ( ) -> Self {
40+ fn fallthrough ( ) -> Self {
3541 Self {
36- can_continue : true ,
42+ can_fall_through : true ,
3743 ..Default :: default ( )
3844 }
3945 }
4046
4147 fn merge_choice ( & mut self , mut other : Self ) {
4248 self . return_points . append ( & mut other. return_points ) ;
43- self . can_continue |= other. can_continue ;
49+ self . can_fall_through |= other. can_fall_through ;
4450 self . can_break |= other. can_break ;
45- self . can_stall |= other. can_stall ;
51+ self . is_infinite |= other. is_infinite ;
52+ self . may_diverge |= other. may_diverge ;
4653 }
4754}
4855
@@ -54,22 +61,22 @@ where
5461 F : FnMut ( & LuaExpr ) -> Result < LuaType , InferFailReason > ,
5562{
5663 let mut flow = analyze_block_returns ( body, infer_expr_type) ?;
57- if flow. can_continue || flow. can_break {
64+ if flow. can_fall_through || flow. can_break {
5865 flow. return_points . push ( LuaReturnPoint :: Nil ) ;
5966 }
6067
6168 Ok ( flow. return_points )
6269}
6370
64- pub fn does_func_body_always_return_or_exit < F > (
71+ pub ( in crate :: compilation :: analyzer ) fn analyze_func_body_missing_return_flags_with < F > (
6572 body : LuaBlock ,
6673 infer_expr_type : & mut F ,
67- ) -> Result < bool , InferFailReason >
74+ ) -> Result < ( bool , bool , bool ) , InferFailReason >
6875where
6976 F : FnMut ( & LuaExpr ) -> Result < LuaType , InferFailReason > ,
7077{
7178 let flow = analyze_block_returns ( body, infer_expr_type) ?;
72- Ok ( ! flow. can_continue && ! flow. can_break && ! flow. can_stall )
79+ Ok ( ( flow. can_fall_through , flow. can_break , flow. is_infinite ) )
7380}
7481
7582fn analyze_block_returns < F > (
@@ -80,21 +87,22 @@ where
8087 F : FnMut ( & LuaExpr ) -> Result < LuaType , InferFailReason > ,
8188{
8289 let mut flow = ReturnFlow :: default ( ) ;
83- let mut can_continue = true ;
90+ let mut can_fall_through = true ;
8491
8592 for stat in block. get_stats ( ) {
86- if !can_continue {
93+ if !can_fall_through {
8794 break ;
8895 }
8996
9097 let stat_flow = analyze_stat_returns ( stat, infer_expr_type) ?;
9198 flow. return_points . extend ( stat_flow. return_points ) ;
9299 flow. can_break |= stat_flow. can_break ;
93- flow. can_stall |= stat_flow. can_stall ;
94- can_continue = stat_flow. can_continue ;
100+ flow. is_infinite |= stat_flow. is_infinite ;
101+ flow. may_diverge |= stat_flow. may_diverge ;
102+ can_fall_through = stat_flow. can_fall_through ;
95103 }
96104
97- flow. can_continue = can_continue ;
105+ flow. can_fall_through = can_fall_through ;
98106 Ok ( flow)
99107}
100108
@@ -107,7 +115,7 @@ where
107115{
108116 match block {
109117 Some ( block) => analyze_block_returns ( block, infer_expr_type) ,
110- None => Ok ( ReturnFlow :: continue_flow ( ) ) ,
118+ None => Ok ( ReturnFlow :: fallthrough ( ) ) ,
111119 }
112120}
113121
@@ -135,7 +143,7 @@ where
135143 ..Default :: default ( )
136144 } ) ,
137145 LuaStat :: ReturnStat ( return_stat) => Ok ( analyze_return_stat_returns ( return_stat) ) ,
138- _ => Ok ( ReturnFlow :: continue_flow ( ) ) ,
146+ _ => Ok ( ReturnFlow :: fallthrough ( ) ) ,
139147 }
140148}
141149
@@ -159,15 +167,31 @@ where
159167 let condition_state =
160168 analyze_condition_state ( while_stat. get_condition_expr ( ) , infer_expr_type) ?;
161169 match condition_state {
162- ConditionState :: Falsy => Ok ( ReturnFlow :: continue_flow ( ) ) ,
170+ ConditionState :: Falsy => Ok ( ReturnFlow :: fallthrough ( ) ) ,
163171 ConditionState :: Never => Ok ( ReturnFlow :: default ( ) ) ,
164- ConditionState :: Truthy | ConditionState :: Dynamic => {
172+ ConditionState :: Truthy => {
165173 let body = analyze_optional_block_returns ( while_stat. get_block ( ) , infer_expr_type) ?;
166174 Ok ( ReturnFlow {
167175 return_points : body. return_points ,
168- can_continue : matches ! ( condition_state , ConditionState :: Dynamic ) || body. can_break ,
176+ can_fall_through : body. can_break ,
169177 can_break : false ,
170- can_stall : body. can_continue || body. can_stall ,
178+ is_infinite : ( body. can_fall_through && !body. can_break ) || body. is_infinite ,
179+ may_diverge : ( body. can_fall_through && body. can_break ) || body. may_diverge ,
180+ } )
181+ }
182+ ConditionState :: Dynamic => {
183+ let body = analyze_optional_block_returns ( while_stat. get_block ( ) , infer_expr_type) ?;
184+ Ok ( ReturnFlow {
185+ return_points : body. return_points ,
186+ can_fall_through : true ,
187+ can_break : false ,
188+ // A dynamic condition may skip the loop entirely, but a body
189+ // that is already proven infinite remains infinite once
190+ // entered.
191+ is_infinite : body. is_infinite ,
192+ // We keep dynamic loops in `may_diverge` rather than trying to
193+ // prove exit from body progress here.
194+ may_diverge : body. can_fall_through || body. may_diverge ,
171195 } )
172196 }
173197 }
@@ -183,25 +207,32 @@ where
183207 let body = analyze_optional_block_returns ( repeat_stat. get_block ( ) , infer_expr_type) ?;
184208 let mut flow = ReturnFlow {
185209 return_points : body. return_points ,
186- can_continue : body. can_break ,
210+ can_fall_through : body. can_break ,
187211 can_break : false ,
188- can_stall : body. can_stall ,
212+ is_infinite : body. is_infinite ,
213+ may_diverge : body. may_diverge ,
189214 } ;
190215
191- if !body. can_continue {
216+ if !body. can_fall_through {
192217 return Ok ( flow) ;
193218 }
194219
195220 match analyze_condition_state ( repeat_stat. get_condition_expr ( ) , infer_expr_type) ? {
196221 ConditionState :: Truthy => {
197- flow. can_continue = true ;
222+ flow. can_fall_through = true ;
198223 }
199224 ConditionState :: Falsy => {
200- flow. can_stall = true ;
225+ if body. can_break {
226+ flow. may_diverge = true ;
227+ } else {
228+ flow. is_infinite = true ;
229+ }
201230 }
202231 ConditionState :: Dynamic => {
203- flow. can_continue = true ;
204- flow. can_stall = true ;
232+ flow. can_fall_through = true ;
233+ // Same rule as dynamic `while`: the loop body is reachable, but
234+ // the exit still depends on runtime state.
235+ flow. may_diverge = true ;
205236 }
206237 ConditionState :: Never => { }
207238 }
@@ -217,7 +248,7 @@ where
217248 F : FnMut ( & LuaExpr ) -> Result < LuaType , InferFailReason > ,
218249{
219250 let mut flow = analyze_optional_block_returns ( for_stat. get_block ( ) , infer_expr_type) ?;
220- flow. can_continue = true ;
251+ flow. can_fall_through = true ;
221252 flow. can_break = false ;
222253 Ok ( flow)
223254}
@@ -286,7 +317,7 @@ where
286317 }
287318
288319 if can_reach_next_clause {
289- flow. can_continue = true ;
320+ flow. can_fall_through = true ;
290321 }
291322
292323 Ok ( flow)
@@ -300,7 +331,7 @@ where
300331 F : FnMut ( & LuaExpr ) -> Result < LuaType , InferFailReason > ,
301332{
302333 let mut flow = analyze_optional_block_returns ( for_range_stat. get_block ( ) , infer_expr_type) ?;
303- flow. can_continue = true ;
334+ flow. can_fall_through = true ;
304335 flow. can_break = false ;
305336 Ok ( flow)
306337}
@@ -309,7 +340,7 @@ fn analyze_call_expr_stat_returns(
309340 call_expr_stat : LuaCallExprStat ,
310341) -> Result < ReturnFlow , InferFailReason > {
311342 let Some ( call_expr) = call_expr_stat. get_call_expr ( ) else {
312- return Ok ( ReturnFlow :: continue_flow ( ) ) ;
343+ return Ok ( ReturnFlow :: fallthrough ( ) ) ;
313344 } ;
314345
315346 if call_expr. is_error ( ) {
@@ -319,7 +350,7 @@ fn analyze_call_expr_stat_returns(
319350 } ) ;
320351 }
321352
322- Ok ( ReturnFlow :: continue_flow ( ) )
353+ Ok ( ReturnFlow :: fallthrough ( ) )
323354}
324355
325356fn analyze_return_stat_returns ( return_stat : LuaReturnStat ) -> ReturnFlow {
0 commit comments