@@ -155,6 +155,12 @@ 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
+
158
164
impl < ' a > CheckLoanCtxt < ' a > {
159
165
pub fn tcx ( & self ) -> & ' a ty:: ctxt { self . bccx . tcx }
160
166
@@ -189,29 +195,75 @@ impl<'a> CheckLoanCtxt<'a> {
189
195
} )
190
196
}
191
197
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`
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.
199
205
200
- self . each_in_scope_loan ( scope_id, |loan| {
201
- debug ! ( "each_in_scope_restriction found loan: {:?}" ,
202
- loan. repr( self . tcx( ) ) ) ;
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
203
212
213
+ let cont = self . each_in_scope_loan ( scope_id, |loan| {
204
214
let mut ret = true ;
205
- for restr in loan. restrictions . iter ( ) {
206
- if * restr . loan_path == * loan_path {
207
- if !op ( loan, restr ) {
215
+ for restr_path in loan. restricted_paths . iter ( ) {
216
+ if * * restr_path == * loan_path {
217
+ if !op ( loan) {
208
218
ret = false ;
209
219
break ;
210
220
}
211
221
}
212
222
}
213
223
ret
214
- } )
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 ;
215
267
}
216
268
217
269
pub fn loans_generated_by ( & self , scope_id : ast:: NodeId ) -> Vec < uint > {
@@ -288,26 +340,12 @@ impl<'a> CheckLoanCtxt<'a> {
288
340
loan1. repr( self . tcx( ) ) ,
289
341
loan2. repr( self . tcx( ) ) ) ;
290
342
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 ,
300
-
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) ;
343
+ if compatible_borrow_kinds ( loan1. kind , loan2. kind ) {
344
+ return true ;
345
+ }
307
346
308
- for restr in loan1. restrictions . iter ( ) {
309
- if !restr. set . intersects ( illegal_if) { continue ; }
310
- if restr. loan_path != loan2. loan_path { continue ; }
347
+ for restr_path in loan1. restricted_paths . iter ( ) {
348
+ if * restr_path != loan2. loan_path { continue ; }
311
349
312
350
let old_pronoun = if new_loan. loan_path == old_loan. loan_path {
313
351
"it" . to_string ( )
@@ -534,63 +572,16 @@ impl<'a> CheckLoanCtxt<'a> {
534
572
535
573
let mut ret = UseOk ;
536
574
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) {
575
+ self . each_in_scope_loan_affecting_path ( expr_id, use_path, |loan| {
576
+ if !compatible_borrow_kinds ( loan. kind , borrow_kind) {
546
577
ret = UseWhileBorrowed ( loan. loan_path . clone ( ) , loan. span ) ;
547
578
false
548
579
} else {
549
580
true
550
581
}
551
582
} ) ;
552
583
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
-
587
584
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
- }
594
585
}
595
586
596
587
fn check_if_path_is_moved ( & self ,
@@ -668,11 +659,9 @@ impl<'a> CheckLoanCtxt<'a> {
668
659
// and aliasing restrictions:
669
660
if assignee_cmt. mutbl . is_mutable ( ) {
670
661
if check_for_aliasable_mutable_writes ( self , 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:
662
+ if mode != euv:: Init {
663
+ check_for_assignment_to_borrowed_path (
664
+ self , assignment_id, assignment_span, assignee_cmt. clone ( ) ) ;
676
665
mark_variable_as_used_mut ( self , assignee_cmt) ;
677
666
}
678
667
}
@@ -807,138 +796,24 @@ impl<'a> CheckLoanCtxt<'a> {
807
796
}
808
797
}
809
798
810
- fn check_for_assignment_to_restricted_or_frozen_location (
799
+ fn check_for_assignment_to_borrowed_path (
811
800
this : & CheckLoanCtxt ,
812
801
assignment_id : ast:: NodeId ,
813
802
assignment_span : Span ,
814
- assignee_cmt : mc:: cmt ) -> bool
803
+ assignee_cmt : mc:: cmt )
815
804
{
816
805
//! Check for assignments that violate the terms of an
817
806
//! outstanding loan.
818
807
819
808
let loan_path = match opt_loan_path ( & assignee_cmt) {
820
809
Some ( lp) => lp,
821
- None => { return true ; /* no loan path, can't be any loans */ }
810
+ None => { return ; /* no loan path, can't be any loans */ }
822
811
} ;
823
812
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
- }
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
863
816
} ) ;
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
- }
942
817
}
943
818
}
944
819
0 commit comments