@@ -58,8 +58,20 @@ import capture::{cap_move, cap_drop, cap_copy, cap_ref};
58
58
export check_crate;
59
59
export last_use_map;
60
60
61
+ // Maps from an expr id to a list of variable ids for which this expr
62
+ // is the last use. Typically, the expr is a path and the node id is
63
+ // the local/argument/etc that the path refers to. However, it also
64
+ // possible for the expr to be a closure, in which case the list is a
65
+ // list of closed over variables that can be moved into the closure.
61
66
type last_use_map = hashmap < node_id , @dvec < node_id > > ;
62
67
68
+ // A set of variable ids which must be spilled (stored on the stack).
69
+ // We add in any variables or arguments where:
70
+ // (1) the variables are moved;
71
+ // (2) the address of the variable/argument is taken;
72
+ // or (3) we find a last use (as they may be moved).
73
+ type spill_map = hashmap < node_id , ( ) > ;
74
+
63
75
enum variable = uint;
64
76
enum live_node = uint;
65
77
@@ -81,7 +93,9 @@ fn check_crate(tcx: ty::ctxt,
81
93
} ) ;
82
94
83
95
let last_use_map = int_hash ( ) ;
84
- let initial_maps = @ir_maps ( tcx, method_map, last_use_map) ;
96
+ let spill_map = int_hash ( ) ;
97
+ let initial_maps = @ir_maps ( tcx, method_map,
98
+ last_use_map, spill_map) ;
85
99
visit:: visit_crate ( * crate , initial_maps, visitor) ;
86
100
tcx. sess . abort_if_errors ( ) ;
87
101
ret last_use_map;
@@ -141,6 +155,7 @@ class ir_maps {
141
155
let tcx: ty:: ctxt;
142
156
let method_map: typeck:: method_map ;
143
157
let last_use_map: last_use_map ;
158
+ let spill_map: spill_map ;
144
159
145
160
let mut num_live_nodes: uint ;
146
161
let mut num_vars: uint ;
@@ -152,10 +167,11 @@ class ir_maps {
152
167
let mut lnks: [ live_node_kind ] ;
153
168
154
169
new ( tcx: ty:: ctxt, method_map: typeck:: method_map,
155
- last_use_map: hashmap<node_id , @dvec<node_id>> ) {
170
+ last_use_map: last_use_map , spill_map : spill_map ) {
156
171
self . tcx = tcx;
157
172
self . method_map = method_map;
158
173
self . last_use_map = last_use_map;
174
+ self . spill_map = spill_map;
159
175
160
176
self . num_live_nodes = 0 u;
161
177
self . num_vars = 0 u;
@@ -236,6 +252,11 @@ class ir_maps {
236
252
expr_id, id, name] ;
237
253
( * v) . push ( id) ;
238
254
}
255
+
256
+ fn add_spill ( var : variable ) {
257
+ let id = self . var_infos [ * var] . id ;
258
+ if id != 0 { self . spill_map . insert ( id, ( ) ) ; }
259
+ }
239
260
}
240
261
241
262
fn visit_fn ( fk : visit:: fn_kind , decl : fn_decl , body : blk ,
@@ -245,7 +266,7 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
245
266
246
267
// swap in a new set of IR maps for this function body:
247
268
let fn_maps = @ir_maps ( self . tcx , self . method_map ,
248
- self . last_use_map ) ;
269
+ self . last_use_map , self . spill_map ) ;
249
270
250
271
#debug[ "creating fn_maps: %x" , ptr:: addr_of ( * fn_maps) as uint ] ;
251
272
@@ -453,6 +474,18 @@ class liveness {
453
474
}
454
475
}
455
476
477
+ fn variable_from_path( expr: @expr) -> option<variable> {
478
+ alt expr. node {
479
+ expr_path( _) {
480
+ let def = self . tcx. def_map. get( expr. id) ;
481
+ relevant_def( def) . map { |rdef|
482
+ self . variable_from_rdef( rdef, expr. span)
483
+ }
484
+ }
485
+ _ { none}
486
+ }
487
+ }
488
+
456
489
fn variable( node_id: node_id, span: span) -> variable {
457
490
( * self . ir) . variable( node_id, span)
458
491
}
@@ -682,11 +715,18 @@ class liveness {
682
715
// inputs passed by & mode should be considered live on exit:
683
716
for decl. inputs. each { |arg|
684
717
alt ty:: resolved_mode( self . tcx, arg. mode) {
685
- by_mutbl_ref {
718
+ by_mutbl_ref | by_ref | by_val {
719
+ // These are "non-owned" modes, so register a read at
720
+ // the end. This will prevent us from moving out of
721
+ // such variables but also prevent us from registering
722
+ // last uses and so forth.
686
723
let var = self . variable( arg. id, blk. span) ;
687
724
self . acc( self . s. exit_ln, var, ACC_READ) ;
688
725
}
689
- by_val | by_ref | by_move | by_copy { }
726
+ by_move | by_copy {
727
+ // These are owned modes. If we don't use the
728
+ // variable, nobody will.
729
+ }
690
730
}
691
731
}
692
732
@@ -1325,7 +1365,11 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) {
1325
1365
vt. visit_expr( f, self , vt) ;
1326
1366
vec:: iter2( args, targs) { |arg_expr, arg_ty|
1327
1367
alt ty:: resolved_mode( self . tcx, arg_ty. mode) {
1328
- by_val | by_ref | by_mutbl_ref | by_copy {
1368
+ by_val | by_copy {
1369
+ vt. visit_expr( arg_expr, self, vt) ;
1370
+ }
1371
+ by_ref | by_mutbl_ref {
1372
+ self. spill_expr( arg_expr) ;
1329
1373
vt. visit_expr( arg_expr, self , vt) ;
1330
1374
}
1331
1375
by_move {
@@ -1335,13 +1379,17 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) {
1335
1379
}
1336
1380
}
1337
1381
1382
+ expr_addr_of( _, arg_expr) {
1383
+ self . spill_expr( arg_expr) ;
1384
+ }
1385
+
1338
1386
// no correctness conditions related to liveness
1339
1387
expr_if_check( * ) | expr_if( * ) | expr_alt( * ) |
1340
1388
expr_while( * ) | expr_loop( * ) |
1341
1389
expr_index( * ) | expr_field( * ) | expr_vstore( * ) |
1342
1390
expr_vec( * ) | expr_rec( * ) | expr_tup( * ) |
1343
1391
expr_bind( * ) | expr_new( * ) | expr_log( * ) | expr_binary( * ) |
1344
- expr_assert( * ) | expr_check( * ) | expr_addr_of ( * ) | expr_copy( * ) |
1392
+ expr_assert( * ) | expr_check( * ) | expr_copy( * ) |
1345
1393
expr_loop_body( * ) | expr_cast( * ) | expr_unary( * ) | expr_fail( * ) |
1346
1394
expr_ret( * ) | expr_break | expr_cont | expr_lit( _) |
1347
1395
expr_block( * ) | expr_swap( * ) | expr_mac( * ) {
@@ -1411,7 +1459,10 @@ impl check_methods for @liveness {
1411
1459
ln. to_str( ) , var. to_str( ) ] ;
1412
1460
1413
1461
alt ( * self ) . live_on_exit( ln, var) {
1414
- none { }
1462
+ none {
1463
+ // update spill map to include this variable, as it is moved:
1464
+ ( * self . ir) . add_spill( var) ;
1465
+ }
1415
1466
some( lnk) {
1416
1467
self . report_illegal_read( span, lnk, var, moved_variable) ;
1417
1468
self . tcx. sess. span_note(
@@ -1426,10 +1477,20 @@ impl check_methods for @liveness {
1426
1477
some( _) { }
1427
1478
none {
1428
1479
( * self . ir) . add_last_use( expr. id, var) ;
1480
+
1481
+ // update spill map to include this variable, as it may be moved:
1482
+ ( * self . ir) . add_spill( var) ;
1429
1483
}
1430
1484
}
1431
1485
}
1432
1486
1487
+ fn spill_expr( expr: @expr) {
1488
+ alt ( * self ) . variable_from_path( expr) {
1489
+ some( var) { ( * self . ir) . add_spill( var) }
1490
+ none { }
1491
+ }
1492
+ }
1493
+
1433
1494
fn check_move_from_expr( expr: @expr, vt: vt<@liveness>) {
1434
1495
#debug[ "check_move_from_expr( node %d: %s) ",
1435
1496
expr. id, expr_to_str( expr) ] ;
@@ -1441,12 +1502,9 @@ impl check_methods for @liveness {
1441
1502
1442
1503
alt expr. node {
1443
1504
expr_path( _) {
1444
- let def = self . tcx. def_map. get( expr. id) ;
1445
- alt relevant_def( def) {
1446
- some( rdef) {
1447
- // Moving from a variable is allowed if is is not live.
1505
+ alt ( * self ) . variable_from_path( expr) {
1506
+ some( var) {
1448
1507
let ln = ( * self ) . live_node( expr. id, expr. span) ;
1449
- let var = ( * self ) . variable_from_rdef( rdef, expr. span) ;
1450
1508
self . check_move_from_var( expr. span, ln, var) ;
1451
1509
}
1452
1510
none { }
@@ -1606,8 +1664,24 @@ impl check_methods for @liveness {
1606
1664
fn warn_about_unused( sp: span, ln: live_node, var: variable) -> bool {
1607
1665
if !( * self ) . used_on_entry( ln, var) {
1608
1666
for self . should_warn( var) . each { |name|
1609
- self . tcx. sess. span_warn(
1610
- sp, #fmt[ "unused variable: `%s`" , name] ) ;
1667
+
1668
+ // annoying: for parameters in funcs like `fn(x: int)
1669
+ // {ret}`, there is only one node, so asking about
1670
+ // assigned_on_exit() is not meaningful.
1671
+ let is_assigned = if ln == self . s. exit_ln {
1672
+ false
1673
+ } else {
1674
+ ( * self ) . assigned_on_exit( ln, var) . is_some( )
1675
+ } ;
1676
+
1677
+ if is_assigned {
1678
+ self . tcx. sess. span_warn(
1679
+ sp, #fmt[ "variable `%s` is assigned to, \
1680
+ but never used", name] ) ;
1681
+ } else {
1682
+ self . tcx. sess. span_warn(
1683
+ sp, #fmt[ "unused variable: `%s`", name] ) ;
1684
+ }
1611
1685
}
1612
1686
ret true;
1613
1687
}
0 commit comments