@@ -33,20 +33,23 @@ use syntax::codemap::span;
33
33
34
34
struct CheckLoanCtxt < ' self > {
35
35
bccx : @BorrowckCtxt ,
36
- dfcx : & ' self LoanDataFlow ,
36
+ dfcx_loans : & ' self LoanDataFlow ,
37
+ move_data : move_data:: FlowedMoveData ,
37
38
all_loans : & ' self [ Loan ] ,
38
39
reported : @mut HashSet < ast:: node_id > ,
39
40
}
40
41
41
42
pub fn check_loans ( bccx : @BorrowckCtxt ,
42
- dfcx : & LoanDataFlow ,
43
+ dfcx_loans : & LoanDataFlow ,
44
+ move_data : move_data:: FlowedMoveData ,
43
45
all_loans : & [ Loan ] ,
44
46
body : & ast:: blk ) {
45
47
debug ! ( "check_loans(body id=%?)" , body. node. id) ;
46
48
47
49
let clcx = @mut CheckLoanCtxt {
48
50
bccx : bccx,
49
- dfcx : dfcx,
51
+ dfcx_loans : dfcx_loans,
52
+ move_data : move_data,
50
53
all_loans : all_loans,
51
54
reported : @mut HashSet :: new ( ) ,
52
55
} ;
@@ -62,7 +65,6 @@ pub fn check_loans(bccx: @BorrowckCtxt,
62
65
63
66
enum MoveError {
64
67
MoveOk ,
65
- MoveFromIllegalCmt ( mc:: cmt ) ,
66
68
MoveWhileBorrowed ( /*loan*/ @LoanPath , /*loan*/ span )
67
69
}
68
70
@@ -79,7 +81,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
79
81
//! are issued for future scopes and thus they may have been
80
82
//! *issued* but not yet be in effect.
81
83
82
- for self . dfcx . each_bit_on_entry( scope_id) |loan_index| {
84
+ for self . dfcx_loans . each_bit_on_entry( scope_id) |loan_index| {
83
85
let loan = & self . all_loans [ loan_index] ;
84
86
if !op ( loan) {
85
87
return false ;
@@ -131,7 +133,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
131
133
//! we encounter `scope_id`.
132
134
133
135
let mut result = ~[ ] ;
134
- for self . dfcx . each_gen_bit( scope_id) |loan_index| {
136
+ for self . dfcx_loans . each_gen_bit( scope_id) |loan_index| {
135
137
result. push ( loan_index) ;
136
138
}
137
139
return result;
@@ -251,6 +253,29 @@ pub impl<'self> CheckLoanCtxt<'self> {
251
253
}
252
254
}
253
255
256
+ fn check_if_path_is_moved ( & self ,
257
+ id : ast:: node_id ,
258
+ span : span ,
259
+ use_kind : MovedValueUseKind ,
260
+ lp : @LoanPath ) {
261
+ /*!
262
+ * Reports an error if `expr` (which should be a path)
263
+ * is using a moved/uninitialized value
264
+ */
265
+
266
+ debug ! ( "check_if_path_is_moved(id=%?, use_kind=%?, lp=%s)" ,
267
+ id, use_kind, lp. repr( self . bccx. tcx) ) ;
268
+ for self . move_data. each_move_of( id, lp) |move, moved_lp| {
269
+ self . bccx . report_use_of_moved_value (
270
+ span,
271
+ use_kind,
272
+ lp,
273
+ move ,
274
+ moved_lp) ;
275
+ return ;
276
+ }
277
+ }
278
+
254
279
fn check_assignment ( & self , expr : @ast:: expr ) {
255
280
// We don't use cat_expr() here because we don't want to treat
256
281
// auto-ref'd parameters in overloaded operators as rvalues.
@@ -261,48 +286,42 @@ pub impl<'self> CheckLoanCtxt<'self> {
261
286
262
287
debug ! ( "check_assignment(cmt=%s)" , cmt. repr( self . tcx( ) ) ) ;
263
288
264
- // check that the value being assigned is declared as mutable
265
- // and report an error otherwise.
266
- match cmt. mutbl {
267
- mc:: McDeclared => {
268
- // OK, but we have to mark arguments as requiring mut
269
- // if they are assigned (other cases are handled by liveness,
270
- // since we need to distinguish local variables assigned
271
- // once vs those assigned multiple times)
272
- match cmt. cat {
273
- mc:: cat_self( * ) |
274
- mc:: cat_arg( * ) => {
275
- mark_variable_as_used_mut ( self , cmt) ;
276
- }
277
- _ => { }
289
+ // Mutable values can be assigned, as long as they obey loans
290
+ // and aliasing restrictions:
291
+ if cmt. mutbl . is_mutable ( ) {
292
+ if check_for_aliasable_mutable_writes ( self , expr, cmt) {
293
+ if check_for_assignment_to_restricted_or_frozen_location (
294
+ self , expr, cmt)
295
+ {
296
+ // Safe, but record for lint pass later:
297
+ mark_variable_as_used_mut ( self , cmt) ;
278
298
}
279
299
}
280
- mc:: McInherited => {
281
- // OK, but we may have to add an entry to `used_mut_nodes`
282
- mark_variable_as_used_mut ( self , cmt) ;
283
- }
284
- mc:: McReadOnly | mc:: McImmutable => {
285
- // Subtle: liveness guarantees that immutable local
286
- // variables are only assigned once, so no need to
287
- // report an error for an assignment to a local
288
- // variable (note also that it is not legal to borrow
289
- // for a local variable before it has been assigned
290
- // for the first time).
291
- if !self . is_local_variable ( cmt) {
292
- self . bccx . span_err (
293
- expr. span ,
294
- fmt ! ( "cannot assign to %s %s"
295
- cmt. mutbl. to_user_str( ) ,
296
- self . bccx. cmt_to_str( cmt) ) ) ;
297
- }
300
+ return ;
301
+ }
302
+
303
+ // For immutable local variables, assignments are legal
304
+ // if they cannot already have been assigned
305
+ if self . is_local_variable ( cmt) {
306
+ assert ! ( cmt. mutbl. is_immutable( ) ) ; // no "const" locals
307
+ let lp = opt_loan_path ( cmt) . get ( ) ;
308
+ for self . move_data. each_assignment_of( expr. id, lp) |assign| {
309
+ self . bccx . report_reassigned_immutable_variable (
310
+ expr. span ,
311
+ lp,
312
+ assign) ;
298
313
return ;
299
314
}
315
+ return ;
300
316
}
301
317
302
- if check_for_aliasable_mutable_writes ( self , expr, cmt) {
303
- check_for_assignment_to_restricted_or_frozen_location (
304
- self , expr, cmt) ;
305
- }
318
+ // Otherwise, just a plain error.
319
+ self . bccx . span_err (
320
+ expr. span ,
321
+ fmt ! ( "cannot assign to %s %s"
322
+ cmt. mutbl. to_user_str( ) ,
323
+ self . bccx. cmt_to_str( cmt) ) ) ;
324
+ return ;
306
325
307
326
fn mark_variable_as_used_mut ( this : & CheckLoanCtxt ,
308
327
cmt : mc:: cmt ) {
@@ -538,12 +557,6 @@ pub impl<'self> CheckLoanCtxt<'self> {
538
557
let cmt = self . bccx . cat_expr ( ex) ;
539
558
match self . analyze_move_out_from_cmt ( cmt) {
540
559
MoveOk => { }
541
- MoveFromIllegalCmt ( _) => {
542
- self . bccx . span_err (
543
- cmt. span ,
544
- fmt ! ( "cannot move out of %s" ,
545
- self . bccx. cmt_to_str( cmt) ) ) ;
546
- }
547
560
MoveWhileBorrowed ( loan_path, loan_span) => {
548
561
self . bccx . span_err (
549
562
cmt. span ,
@@ -561,29 +574,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
561
574
}
562
575
563
576
fn analyze_move_out_from_cmt ( & self , cmt : mc:: cmt ) -> MoveError {
564
- debug ! ( "check_move_out_from_cmt(cmt=%s)" , cmt. repr( self . tcx( ) ) ) ;
565
-
566
- match cmt. cat {
567
- // Rvalues, locals, and arguments can be moved:
568
- mc:: cat_rvalue | mc:: cat_local( _) |
569
- mc:: cat_arg( _) | mc:: cat_self( _) => { }
570
-
571
- // It seems strange to allow a move out of a static item,
572
- // but what happens in practice is that you have a
573
- // reference to a constant with a type that should be
574
- // moved, like `None::<~int>`. The type of this constant
575
- // is technically `Option<~int>`, which moves, but we know
576
- // that the content of static items will never actually
577
- // contain allocated pointers, so we can just memcpy it.
578
- mc:: cat_static_item => { }
579
-
580
- mc:: cat_deref( _, _, mc:: unsafe_ptr( * ) ) => { }
581
-
582
- // Nothing else.
583
- _ => {
584
- return MoveFromIllegalCmt ( cmt) ;
585
- }
586
- }
577
+ debug ! ( "analyze_move_out_from_cmt(cmt=%s)" , cmt. repr( self . tcx( ) ) ) ;
587
578
588
579
// FIXME(#4384) inadequare if/when we permit `move a.b`
589
580
@@ -631,54 +622,53 @@ fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
631
622
632
623
visit:: fk_anon( * ) |
633
624
visit:: fk_fn_block( * ) => {
634
- let fty = ty:: node_id_to_type ( this. tcx ( ) , id) ;
635
- let fty_sigil = ty:: ty_closure_sigil ( fty) ;
636
- check_moves_from_captured_variables ( this, id, fty_sigil) ;
625
+ check_captured_variables ( this, id, sp) ;
637
626
}
638
627
}
639
628
640
629
visit:: visit_fn ( fk, decl, body, sp, id, this, visitor) ;
641
630
642
- fn check_moves_from_captured_variables ( this : @mut CheckLoanCtxt ,
643
- id : ast:: node_id ,
644
- fty_sigil : ast:: Sigil ) {
645
- match fty_sigil {
646
- ast:: ManagedSigil | ast:: OwnedSigil => {
647
- let cap_vars = this. bccx . capture_map . get ( & id) ;
648
- for cap_vars. each |cap_var| {
649
- match cap_var. mode {
650
- moves:: CapRef | moves:: CapCopy => { loop ; }
651
- moves:: CapMove => { }
652
- }
653
- let def_id = ast_util:: def_id_of_def ( cap_var. def ) . node ;
654
- let ty = ty:: node_id_to_type ( this. tcx ( ) , def_id) ;
655
- let cmt = this. bccx . cat_def ( id, cap_var. span ,
656
- ty, cap_var. def ) ;
657
- let move_err = this. analyze_move_out_from_cmt ( cmt) ;
658
- match move_err {
659
- MoveOk => { }
660
- MoveFromIllegalCmt ( move_cmt) => {
661
- this. bccx . span_err (
662
- cap_var. span ,
663
- fmt ! ( "illegal by-move capture of %s" ,
664
- this. bccx. cmt_to_str( move_cmt) ) ) ;
665
- }
666
- MoveWhileBorrowed ( loan_path, loan_span) => {
667
- this. bccx . span_err (
668
- cap_var. span ,
669
- fmt ! ( "cannot move `%s` into closure \
670
- because it is borrowed",
671
- this. bccx. loan_path_to_str( loan_path) ) ) ;
672
- this. bccx . span_note (
673
- loan_span,
674
- fmt ! ( "borrow of `%s` occurs here" ,
675
- this. bccx. loan_path_to_str( loan_path) ) ) ;
676
- }
677
- }
631
+ fn check_captured_variables ( this : @mut CheckLoanCtxt ,
632
+ closure_id : ast:: node_id ,
633
+ span : span ) {
634
+ let cap_vars = this. bccx . capture_map . get ( & closure_id) ;
635
+ for cap_vars. each |cap_var| {
636
+ match cap_var. mode {
637
+ moves:: CapRef | moves:: CapCopy => {
638
+ let var_id = ast_util:: def_id_of_def ( cap_var. def ) . node ;
639
+ let lp = @LpVar ( var_id) ;
640
+ this. check_if_path_is_moved ( closure_id, span,
641
+ MovedInCapture , lp) ;
642
+ }
643
+ moves:: CapMove => {
644
+ check_by_move_capture ( this, closure_id, cap_var) ;
645
+ }
646
+ }
647
+ }
648
+ return ;
649
+
650
+ fn check_by_move_capture ( this : @mut CheckLoanCtxt ,
651
+ closure_id : ast:: node_id ,
652
+ cap_var : & moves:: CaptureVar ) {
653
+ let var_id = ast_util:: def_id_of_def ( cap_var. def ) . node ;
654
+ let ty = ty:: node_id_to_type ( this. tcx ( ) , var_id) ;
655
+ let cmt = this. bccx . cat_def ( closure_id, cap_var. span ,
656
+ ty, cap_var. def ) ;
657
+ let move_err = this. analyze_move_out_from_cmt ( cmt) ;
658
+ match move_err {
659
+ MoveOk => { }
660
+ MoveWhileBorrowed ( loan_path, loan_span) => {
661
+ this. bccx . span_err (
662
+ cap_var. span ,
663
+ fmt ! ( "cannot move `%s` into closure \
664
+ because it is borrowed",
665
+ this. bccx. loan_path_to_str( loan_path) ) ) ;
666
+ this. bccx . span_note (
667
+ loan_span,
668
+ fmt ! ( "borrow of `%s` occurs here" ,
669
+ this. bccx. loan_path_to_str( loan_path) ) ) ;
678
670
}
679
671
}
680
-
681
- ast:: BorrowedSigil => { }
682
672
}
683
673
}
684
674
}
@@ -692,18 +682,29 @@ fn check_loans_in_local<'a>(local: @ast::local,
692
682
fn check_loans_in_expr < ' a > ( expr : @ast:: expr ,
693
683
this : @mut CheckLoanCtxt < ' a > ,
694
684
vt : visit:: vt < @mut CheckLoanCtxt < ' a > > ) {
685
+ visit:: visit_expr ( expr, this, vt) ;
686
+
695
687
debug ! ( "check_loans_in_expr(expr=%s)" ,
696
688
expr. repr( this. tcx( ) ) ) ;
697
689
698
- visit:: visit_expr ( expr, this, vt) ;
699
-
700
690
this. check_for_conflicting_loans ( expr. id ) ;
701
691
702
692
if this. bccx . moves_map . contains ( & expr. id ) {
703
693
this. check_move_out_from_expr ( expr) ;
704
694
}
705
695
706
696
match expr. node {
697
+ ast:: expr_self |
698
+ ast:: expr_path( * ) => {
699
+ if !this. move_data . is_assignee ( expr. id ) {
700
+ let cmt = this. bccx . cat_expr_unadjusted ( expr) ;
701
+ debug ! ( "path cmt=%s" , cmt. repr( this. tcx( ) ) ) ;
702
+ for opt_loan_path( cmt) . each |& lp| {
703
+ this. check_if_path_is_moved ( expr. id , expr. span ,
704
+ MovedInUse , lp) ;
705
+ }
706
+ }
707
+ }
707
708
ast:: expr_assign( dest, _) |
708
709
ast:: expr_assign_op( _, dest, _) => {
709
710
this. check_assignment ( dest) ;
0 commit comments