@@ -155,12 +155,6 @@ enum UseError {
155
155
UseWhileBorrowed ( /*loan*/ Rc < LoanPath > , /*loan*/ Span )
156
156
}
157
157
158
- fn compatible_borrow_kinds ( borrow_kind1 : ty:: BorrowKind ,
159
- borrow_kind2 : ty:: BorrowKind )
160
- -> bool {
161
- borrow_kind1 == ty:: ImmBorrow && borrow_kind2 == ty:: ImmBorrow
162
- }
163
-
164
158
impl < ' a > CheckLoanCtxt < ' a > {
165
159
pub fn tcx ( & self ) -> & ' a ty:: ctxt { self . bccx . tcx }
166
160
@@ -195,75 +189,29 @@ impl<'a> CheckLoanCtxt<'a> {
195
189
} )
196
190
}
197
191
198
- fn each_in_scope_loan_affecting_path ( & self ,
199
- scope_id : ast:: NodeId ,
200
- loan_path : & LoanPath ,
201
- op: |& Loan | -> bool)
202
- -> bool {
203
- //! Iterates through all of the in-scope loans affecting `loan_path`,
204
- //! calling `op`, and ceasing iteration if `false` is returned.
192
+ pub fn each_in_scope_restriction ( & self ,
193
+ scope_id : ast:: NodeId ,
194
+ loan_path : & LoanPath ,
195
+ op : |& Loan , & Restriction | -> bool)
196
+ -> bool {
197
+ //! Iterates through all the in-scope restrictions for the
198
+ //! given `loan_path`
205
199
206
- // First, we check for a loan restricting the path P being used. This
207
- // accounts for borrows of P but also borrows of subpaths, like P.a.b.
208
- // Consider the following example:
209
- //
210
- // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
211
- // let y = a; // Conflicts with restriction
200
+ self . each_in_scope_loan ( scope_id, |loan| {
201
+ debug ! ( "each_in_scope_restriction found loan: {:?}" ,
202
+ loan. repr( self . tcx( ) ) ) ;
212
203
213
- let cont = self . each_in_scope_loan ( scope_id, |loan| {
214
204
let mut ret = true ;
215
- for restr_path in loan. restricted_paths . iter ( ) {
216
- if * * restr_path == * loan_path {
217
- if !op ( loan) {
205
+ for restr in loan. restrictions . iter ( ) {
206
+ if * restr . loan_path == * loan_path {
207
+ if !op ( loan, restr ) {
218
208
ret = false ;
219
209
break ;
220
210
}
221
211
}
222
212
}
223
213
ret
224
- } ) ;
225
-
226
- if !cont {
227
- return false ;
228
- }
229
-
230
- // Next, we must check for *loans* (not restrictions) on the path P or
231
- // any base path. This rejects examples like the following:
232
- //
233
- // let x = &mut a.b;
234
- // let y = a.b.c;
235
- //
236
- // Limiting this search to *loans* and not *restrictions* means that
237
- // examples like the following continue to work:
238
- //
239
- // let x = &mut a.b;
240
- // let y = a.c;
241
-
242
- let mut loan_path = loan_path;
243
- loop {
244
- match * loan_path {
245
- LpVar ( _) => {
246
- break ;
247
- }
248
- LpExtend ( ref lp_base, _, _) => {
249
- loan_path = & * * lp_base;
250
- }
251
- }
252
-
253
- let cont = self . each_in_scope_loan ( scope_id, |loan| {
254
- if * loan. loan_path == * loan_path {
255
- op ( loan)
256
- } else {
257
- true
258
- }
259
- } ) ;
260
-
261
- if !cont {
262
- return false ;
263
- }
264
- }
265
-
266
- return true ;
214
+ } )
267
215
}
268
216
269
217
pub fn loans_generated_by ( & self , scope_id : ast:: NodeId ) -> Vec < uint > {
@@ -340,12 +288,26 @@ impl<'a> CheckLoanCtxt<'a> {
340
288
loan1. repr( self . tcx( ) ) ,
341
289
loan2. repr( self . tcx( ) ) ) ;
342
290
343
- if compatible_borrow_kinds ( loan1. kind , loan2. kind ) {
344
- return true ;
345
- }
291
+ // Restrictions that would cause the new loan to be illegal:
292
+ let illegal_if = match loan2. kind {
293
+ // Look for restrictions against mutation. These are
294
+ // generated by all other borrows.
295
+ ty:: MutBorrow => RESTR_MUTATE ,
296
+
297
+ // Look for restrictions against freezing (immutable borrows).
298
+ // These are generated by `&mut` borrows.
299
+ ty:: ImmBorrow => RESTR_FREEZE ,
346
300
347
- for restr_path in loan1. restricted_paths . iter ( ) {
348
- if * restr_path != loan2. loan_path { continue ; }
301
+ // No matter how the data is borrowed (as `&`, as `&mut`,
302
+ // or as `&unique imm`) it will always generate a
303
+ // restriction against mutating the data. So look for those.
304
+ ty:: UniqueImmBorrow => RESTR_MUTATE ,
305
+ } ;
306
+ debug ! ( "illegal_if={:?}" , illegal_if) ;
307
+
308
+ for restr in loan1. restrictions . iter ( ) {
309
+ if !restr. set . intersects ( illegal_if) { continue ; }
310
+ if restr. loan_path != loan2. loan_path { continue ; }
349
311
350
312
let old_pronoun = if new_loan. loan_path == old_loan. loan_path {
351
313
"it" . to_string ( )
@@ -572,16 +534,63 @@ impl<'a> CheckLoanCtxt<'a> {
572
534
573
535
let mut ret = UseOk ;
574
536
575
- self . each_in_scope_loan_affecting_path ( expr_id, use_path, |loan| {
576
- if !compatible_borrow_kinds ( loan. kind , borrow_kind) {
537
+ // First, we check for a restriction on the path P being used. This
538
+ // accounts for borrows of P but also borrows of subpaths, like P.a.b.
539
+ // Consider the following example:
540
+ //
541
+ // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
542
+ // let y = a; // Conflicts with restriction
543
+
544
+ self . each_in_scope_restriction ( expr_id, use_path, |loan, _restr| {
545
+ if incompatible ( loan. kind , borrow_kind) {
577
546
ret = UseWhileBorrowed ( loan. loan_path . clone ( ) , loan. span ) ;
578
547
false
579
548
} else {
580
549
true
581
550
}
582
551
} ) ;
583
552
553
+ // Next, we must check for *loans* (not restrictions) on the path P or
554
+ // any base path. This rejects examples like the following:
555
+ //
556
+ // let x = &mut a.b;
557
+ // let y = a.b.c;
558
+ //
559
+ // Limiting this search to *loans* and not *restrictions* means that
560
+ // examples like the following continue to work:
561
+ //
562
+ // let x = &mut a.b;
563
+ // let y = a.c;
564
+
565
+ let mut loan_path = use_path;
566
+ loop {
567
+ self . each_in_scope_loan ( expr_id, |loan| {
568
+ if * loan. loan_path == * loan_path &&
569
+ incompatible ( loan. kind , borrow_kind) {
570
+ ret = UseWhileBorrowed ( loan. loan_path . clone ( ) , loan. span ) ;
571
+ false
572
+ } else {
573
+ true
574
+ }
575
+ } ) ;
576
+
577
+ match * loan_path {
578
+ LpVar ( _) => {
579
+ break ;
580
+ }
581
+ LpExtend ( ref lp_base, _, _) => {
582
+ loan_path = & * * lp_base;
583
+ }
584
+ }
585
+ }
586
+
584
587
return ret;
588
+
589
+ fn incompatible ( borrow_kind1 : ty:: BorrowKind ,
590
+ borrow_kind2 : ty:: BorrowKind )
591
+ -> bool {
592
+ borrow_kind1 != ty:: ImmBorrow || borrow_kind2 != ty:: ImmBorrow
593
+ }
585
594
}
586
595
587
596
fn check_if_path_is_moved ( & self ,
@@ -659,9 +668,11 @@ impl<'a> CheckLoanCtxt<'a> {
659
668
// and aliasing restrictions:
660
669
if assignee_cmt. mutbl . is_mutable ( ) {
661
670
if check_for_aliasable_mutable_writes ( self , assignment_span, assignee_cmt. clone ( ) ) {
662
- if mode != euv:: Init {
663
- check_for_assignment_to_borrowed_path (
664
- self , assignment_id, assignment_span, assignee_cmt. clone ( ) ) ;
671
+ if mode != euv:: Init &&
672
+ check_for_assignment_to_restricted_or_frozen_location (
673
+ self , assignment_id, assignment_span, assignee_cmt. clone ( ) )
674
+ {
675
+ // Safe, but record for lint pass later:
665
676
mark_variable_as_used_mut ( self , assignee_cmt) ;
666
677
}
667
678
}
@@ -796,24 +807,138 @@ impl<'a> CheckLoanCtxt<'a> {
796
807
}
797
808
}
798
809
799
- fn check_for_assignment_to_borrowed_path (
810
+ fn check_for_assignment_to_restricted_or_frozen_location (
800
811
this : & CheckLoanCtxt ,
801
812
assignment_id : ast:: NodeId ,
802
813
assignment_span : Span ,
803
- assignee_cmt : mc:: cmt )
814
+ assignee_cmt : mc:: cmt ) -> bool
804
815
{
805
816
//! Check for assignments that violate the terms of an
806
817
//! outstanding loan.
807
818
808
819
let loan_path = match opt_loan_path ( & assignee_cmt) {
809
820
Some ( lp) => lp,
810
- None => { return ; /* no loan path, can't be any loans */ }
821
+ None => { return true ; /* no loan path, can't be any loans */ }
811
822
} ;
812
823
813
- this. each_in_scope_loan_affecting_path ( assignment_id, & * loan_path, |loan| {
814
- this. report_illegal_mutation ( assignment_span, & * loan_path, loan) ;
815
- false
824
+ // Start by searching for an assignment to a *restricted*
825
+ // location. Here is one example of the kind of error caught
826
+ // by this check:
827
+ //
828
+ // let mut v = ~[1, 2, 3];
829
+ // let p = &v;
830
+ // v = ~[4];
831
+ //
832
+ // In this case, creating `p` triggers a RESTR_MUTATE
833
+ // restriction on the path `v`.
834
+ //
835
+ // Here is a second, more subtle example:
836
+ //
837
+ // let mut v = ~[1, 2, 3];
838
+ // let p = &const v[0];
839
+ // v[0] = 4; // OK
840
+ // v[1] = 5; // OK
841
+ // v = ~[4, 5, 3]; // Error
842
+ //
843
+ // In this case, `p` is pointing to `v[0]`, and it is a
844
+ // `const` pointer in any case. So the first two
845
+ // assignments are legal (and would be permitted by this
846
+ // check). However, the final assignment (which is
847
+ // logically equivalent) is forbidden, because it would
848
+ // cause the existing `v` array to be freed, thus
849
+ // invalidating `p`. In the code, this error results
850
+ // because `gather_loans::restrictions` adds a
851
+ // `RESTR_MUTATE` restriction whenever the contents of an
852
+ // owned pointer are borrowed, and hence while `v[*]` is not
853
+ // restricted from being written, `v` is.
854
+ let cont = this. each_in_scope_restriction ( assignment_id,
855
+ & * loan_path,
856
+ |loan, restr| {
857
+ if restr. set . intersects ( RESTR_MUTATE ) {
858
+ this. report_illegal_mutation ( assignment_span, & * loan_path, loan) ;
859
+ false
860
+ } else {
861
+ true
862
+ }
816
863
} ) ;
864
+
865
+ if !cont { return false }
866
+
867
+ // The previous code handled assignments to paths that
868
+ // have been restricted. This covers paths that have been
869
+ // directly lent out and their base paths, but does not
870
+ // cover random extensions of those paths. For example,
871
+ // the following program is not declared illegal by the
872
+ // previous check:
873
+ //
874
+ // let mut v = ~[1, 2, 3];
875
+ // let p = &v;
876
+ // v[0] = 4; // declared error by loop below, not code above
877
+ //
878
+ // The reason that this passes the previous check whereas
879
+ // an assignment like `v = ~[4]` fails is because the assignment
880
+ // here is to `v[*]`, and the existing restrictions were issued
881
+ // for `v`, not `v[*]`.
882
+ //
883
+ // So in this loop, we walk back up the loan path so long
884
+ // as the mutability of the path is dependent on a super
885
+ // path, and check that the super path was not lent out as
886
+ // mutable or immutable (a const loan is ok).
887
+ //
888
+ // Mutability of a path can be dependent on the super path
889
+ // in two ways. First, it might be inherited mutability.
890
+ // Second, the pointee of an `&mut` pointer can only be
891
+ // mutated if it is found in an unaliased location, so we
892
+ // have to check that the owner location is not borrowed.
893
+ //
894
+ // Note that we are *not* checking for any and all
895
+ // restrictions. We are only interested in the pointers
896
+ // that the user created, whereas we add restrictions for
897
+ // all kinds of paths that are not directly aliased. If we checked
898
+ // for all restrictions, and not just loans, then the following
899
+ // valid program would be considered illegal:
900
+ //
901
+ // let mut v = ~[1, 2, 3];
902
+ // let p = &const v[0];
903
+ // v[1] = 5; // ok
904
+ //
905
+ // Here the restriction that `v` not be mutated would be misapplied
906
+ // to block the subpath `v[1]`.
907
+ let full_loan_path = loan_path. clone ( ) ;
908
+ let mut loan_path = loan_path;
909
+ loop {
910
+ loan_path = match * loan_path {
911
+ // Peel back one layer if, for `loan_path` to be
912
+ // mutable, `lp_base` must be mutable. This occurs
913
+ // with inherited mutability, owned pointers and
914
+ // `&mut` pointers.
915
+ LpExtend ( ref lp_base, mc:: McInherited , _) |
916
+ LpExtend ( ref lp_base, _, LpDeref ( mc:: OwnedPtr ) ) |
917
+ LpExtend ( ref lp_base, _, LpDeref ( mc:: GcPtr ) ) |
918
+ LpExtend ( ref lp_base, _, LpDeref ( mc:: BorrowedPtr ( ty:: MutBorrow , _) ) ) => {
919
+ lp_base. clone ( )
920
+ }
921
+
922
+ // Otherwise stop iterating
923
+ LpExtend ( _, mc:: McDeclared , _) |
924
+ LpExtend ( _, mc:: McImmutable , _) |
925
+ LpVar ( _) => {
926
+ return true ;
927
+ }
928
+ } ;
929
+
930
+ // Check for a non-const loan of `loan_path`
931
+ let cont = this. each_in_scope_loan ( assignment_id, |loan| {
932
+ if loan. loan_path == loan_path {
933
+ this. report_illegal_mutation ( assignment_span, & * full_loan_path, loan) ;
934
+ false
935
+ } else {
936
+ true
937
+ }
938
+ } ) ;
939
+
940
+ if !cont { return false }
941
+ }
817
942
}
818
943
}
819
944
0 commit comments