100
100
* - `no_ret_var`: a synthetic variable that is only 'read' from, the
101
101
* fallthrough node. This allows us to detect functions where we fail
102
102
* to return explicitly.
103
+ * - `clean_exit_var`: a synthetic variable that is only 'read' from the
104
+ * fallthrough node. It is only live if the function could converge
105
+ * via means other than an explicit `return` expression. That is, it is
106
+ * only dead if the end of the function's block can never be reached.
107
+ * It is the responsibility of typeck to ensure that there are no
108
+ * `return` expressions in a function declared as diverging.
103
109
*/
104
110
105
111
use middle:: def:: * ;
106
112
use middle:: mem_categorization:: Typer ;
107
113
use middle:: pat_util;
114
+ use middle:: typeck;
108
115
use middle:: ty;
109
116
use lint;
110
117
use util:: nodemap:: NodeMap ;
@@ -250,7 +257,8 @@ struct LocalInfo {
250
257
enum VarKind {
251
258
Arg ( NodeId , Ident ) ,
252
259
Local ( LocalInfo ) ,
253
- ImplicitRet
260
+ ImplicitRet ,
261
+ CleanExit
254
262
}
255
263
256
264
struct IrMaps < ' a , ' tcx : ' a > {
@@ -306,7 +314,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
306
314
Local ( LocalInfo { id : node_id, .. } ) | Arg ( node_id, _) => {
307
315
self . variable_map . insert ( node_id, v) ;
308
316
} ,
309
- ImplicitRet => { }
317
+ ImplicitRet | CleanExit => { }
310
318
}
311
319
312
320
debug ! ( "{} is {}" , v. to_string( ) , vk) ;
@@ -331,7 +339,8 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
331
339
Local ( LocalInfo { ident : nm, .. } ) | Arg ( _, nm) => {
332
340
token:: get_ident ( nm) . get ( ) . to_string ( )
333
341
} ,
334
- ImplicitRet => "<implicit-ret>" . to_string ( )
342
+ ImplicitRet => "<implicit-ret>" . to_string ( ) ,
343
+ CleanExit => "<clean-exit>" . to_string ( )
335
344
}
336
345
}
337
346
@@ -397,7 +406,8 @@ fn visit_fn(ir: &mut IrMaps,
397
406
let specials = Specials {
398
407
exit_ln : fn_maps. add_live_node ( ExitNode ) ,
399
408
fallthrough_ln : fn_maps. add_live_node ( ExitNode ) ,
400
- no_ret_var : fn_maps. add_variable ( ImplicitRet )
409
+ no_ret_var : fn_maps. add_variable ( ImplicitRet ) ,
410
+ clean_exit_var : fn_maps. add_variable ( CleanExit )
401
411
} ;
402
412
403
413
// compute liveness
@@ -546,7 +556,8 @@ fn invalid_users() -> Users {
546
556
struct Specials {
547
557
exit_ln : LiveNode ,
548
558
fallthrough_ln : LiveNode ,
549
- no_ret_var : Variable
559
+ no_ret_var : Variable ,
560
+ clean_exit_var : Variable
550
561
}
551
562
552
563
static ACC_READ : uint = 1 u;
@@ -873,6 +884,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
873
884
if blk. expr . is_none ( ) {
874
885
self . acc ( s. fallthrough_ln , s. no_ret_var , ACC_READ )
875
886
}
887
+ self . acc ( s. fallthrough_ln , s. clean_exit_var , ACC_READ ) ;
876
888
877
889
self . propagate_through_block ( blk, s. fallthrough_ln )
878
890
}
@@ -943,9 +955,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
943
955
opt_expr : Option < & Expr > ,
944
956
succ : LiveNode )
945
957
-> LiveNode {
946
- opt_expr. iter ( ) . fold ( succ, |succ, expr| {
947
- self . propagate_through_expr ( & * * expr, succ)
948
- } )
958
+ opt_expr. map_or ( succ, |expr| self . propagate_through_expr ( expr, succ) )
949
959
}
950
960
951
961
fn propagate_through_expr ( & mut self , expr : & Expr , succ : LiveNode )
@@ -1146,13 +1156,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
1146
1156
}
1147
1157
1148
1158
ExprCall ( ref f, ref args) => {
1149
- // calling a fn with bot return type means that the fn
1150
- // will fail, and hence the successors can be ignored
1151
- let is_bot = !self . ir . tcx . is_method_call ( expr. id ) && {
1159
+ let diverges = !self . ir . tcx . is_method_call ( expr. id ) && {
1152
1160
let t_ret = ty:: ty_fn_ret ( ty:: expr_ty ( self . ir . tcx , & * * f) ) ;
1153
- ty:: type_is_bot ( t_ret )
1161
+ t_ret == ty:: FnDiverging
1154
1162
} ;
1155
- let succ = if is_bot {
1163
+ let succ = if diverges {
1156
1164
self . s . exit_ln
1157
1165
} else {
1158
1166
succ
@@ -1162,11 +1170,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
1162
1170
}
1163
1171
1164
1172
ExprMethodCall ( _, _, ref args) => {
1165
- // calling a method with bot return type means that the method
1166
- // will fail, and hence the successors can be ignored
1167
- let t_ret = ty:: node_id_to_type ( self . ir . tcx , expr. id ) ;
1168
- let succ = if ty:: type_is_bot ( t_ret) { self . s . exit_ln }
1169
- else { succ} ;
1173
+ let method_call = typeck:: MethodCall :: expr ( expr. id ) ;
1174
+ let method_ty = self . ir . tcx . method_map . borrow ( ) . find ( & method_call) . unwrap ( ) . ty ;
1175
+ let diverges = ty:: ty_fn_ret ( method_ty) == ty:: FnDiverging ;
1176
+ let succ = if diverges {
1177
+ self . s . exit_ln
1178
+ } else {
1179
+ succ
1180
+ } ;
1170
1181
self . propagate_through_exprs ( args. as_slice ( ) , succ)
1171
1182
}
1172
1183
@@ -1507,50 +1518,68 @@ fn check_fn(_v: &Liveness,
1507
1518
}
1508
1519
1509
1520
impl < ' a , ' tcx > Liveness < ' a , ' tcx > {
1521
+ fn fn_ret ( & self , id : NodeId ) -> ty:: FnOutput {
1522
+ let fn_ty = ty:: node_id_to_type ( self . ir . tcx , id) ;
1523
+ match ty:: get ( fn_ty) . sty {
1524
+ ty:: ty_unboxed_closure( closure_def_id, _, _) =>
1525
+ self . ir . tcx . unboxed_closures ( )
1526
+ . borrow ( )
1527
+ . find ( & closure_def_id)
1528
+ . unwrap ( )
1529
+ . closure_type
1530
+ . sig
1531
+ . output ,
1532
+ _ => ty:: ty_fn_ret ( fn_ty)
1533
+ }
1534
+ }
1535
+
1510
1536
fn check_ret ( & self ,
1511
1537
id : NodeId ,
1512
1538
sp : Span ,
1513
1539
_fk : FnKind ,
1514
1540
entry_ln : LiveNode ,
1515
1541
body : & Block ) {
1516
- if self . live_on_entry ( entry_ln, self . s . no_ret_var ) . is_some ( ) {
1517
- // if no_ret_var is live, then we fall off the end of the
1518
- // function without any kind of return expression:
1519
-
1520
- let t_ret = ty:: ty_fn_ret ( ty:: node_id_to_type ( self . ir . tcx , id) ) ;
1521
- if ty:: type_is_nil ( t_ret) {
1522
- // for nil return types, it is ok to not return a value expl.
1523
- } else if ty:: type_is_bot ( t_ret) {
1524
- // for bot return types, not ok. Function should fail.
1525
- self . ir . tcx . sess . span_err (
1526
- sp, "some control paths may return" ) ;
1527
- } else {
1528
- let ends_with_stmt = match body. expr {
1529
- None if body. stmts . len ( ) > 0 =>
1530
- match body. stmts . last ( ) . unwrap ( ) . node {
1531
- StmtSemi ( ref e, _) => {
1532
- let t_stmt = ty:: expr_ty ( self . ir . tcx , & * * e) ;
1533
- ty:: get ( t_stmt) . sty == ty:: get ( t_ret) . sty
1542
+ match self . fn_ret ( id) {
1543
+ ty:: FnConverging ( t_ret)
1544
+ if self . live_on_entry ( entry_ln, self . s . no_ret_var ) . is_some ( ) => {
1545
+
1546
+ if ty:: type_is_nil ( t_ret) {
1547
+ // for nil return types, it is ok to not return a value expl.
1548
+ } else {
1549
+ let ends_with_stmt = match body. expr {
1550
+ None if body. stmts . len ( ) > 0 =>
1551
+ match body. stmts . last ( ) . unwrap ( ) . node {
1552
+ StmtSemi ( ref e, _) => {
1553
+ let t_stmt = ty:: expr_ty ( self . ir . tcx , & * * e) ;
1554
+ ty:: get ( t_stmt) . sty == ty:: get ( t_ret) . sty
1555
+ } ,
1556
+ _ => false
1534
1557
} ,
1535
- _ => false
1536
- } ,
1537
- _ => false
1538
- } ;
1539
- self . ir . tcx . sess . span_err (
1540
- sp, "not all control paths return a value" ) ;
1541
- if ends_with_stmt {
1542
- let last_stmt = body. stmts . last ( ) . unwrap ( ) ;
1543
- let original_span = original_sp ( self . ir . tcx . sess . codemap ( ) ,
1544
- last_stmt. span , sp) ;
1545
- let span_semicolon = Span {
1546
- lo : original_span. hi - BytePos ( 1 ) ,
1547
- hi : original_span. hi ,
1548
- expn_id : original_span. expn_id
1558
+ _ => false
1549
1559
} ;
1550
- self . ir . tcx . sess . span_note (
1551
- span_semicolon, "consider removing this semicolon:" ) ;
1560
+ self . ir . tcx . sess . span_err (
1561
+ sp, "not all control paths return a value" ) ;
1562
+ if ends_with_stmt {
1563
+ let last_stmt = body. stmts . last ( ) . unwrap ( ) ;
1564
+ let original_span = original_sp ( self . ir . tcx . sess . codemap ( ) ,
1565
+ last_stmt. span , sp) ;
1566
+ let span_semicolon = Span {
1567
+ lo : original_span. hi - BytePos ( 1 ) ,
1568
+ hi : original_span. hi ,
1569
+ expn_id : original_span. expn_id
1570
+ } ;
1571
+ self . ir . tcx . sess . span_note (
1572
+ span_semicolon, "consider removing this semicolon:" ) ;
1573
+ }
1552
1574
}
1553
- }
1575
+ }
1576
+ ty:: FnDiverging
1577
+ if self . live_on_entry ( entry_ln, self . s . clean_exit_var ) . is_some ( ) => {
1578
+ self . ir . tcx . sess . span_err ( sp,
1579
+ "computation may converge in a function marked as diverging" ) ;
1580
+ }
1581
+
1582
+ _ => { }
1554
1583
}
1555
1584
}
1556
1585
0 commit comments