@@ -155,6 +155,12 @@ enum UseError {
155155 UseWhileBorrowed ( /*loan*/ Rc < LoanPath > , /*loan*/ Span )
156156}
157157
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+
158164impl < ' a > CheckLoanCtxt < ' a > {
159165 pub fn tcx ( & self ) -> & ' a ty:: ctxt { self . bccx . tcx }
160166
@@ -189,29 +195,75 @@ impl<'a> CheckLoanCtxt<'a> {
189195 } )
190196 }
191197
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.
199205
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
203212
213+ let cont = self . each_in_scope_loan ( scope_id, |loan| {
204214 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) {
208218 ret = false ;
209219 break ;
210220 }
211221 }
212222 }
213223 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 ;
215267 }
216268
217269 pub fn loans_generated_by ( & self , scope_id : ast:: NodeId ) -> Vec < uint > {
@@ -288,26 +340,12 @@ impl<'a> CheckLoanCtxt<'a> {
288340 loan1. repr( self . tcx( ) ) ,
289341 loan2. repr( self . tcx( ) ) ) ;
290342
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+ }
307346
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 ; }
311349
312350 let old_pronoun = if new_loan. loan_path == old_loan. loan_path {
313351 "it" . to_string ( )
@@ -534,63 +572,16 @@ impl<'a> CheckLoanCtxt<'a> {
534572
535573 let mut ret = UseOk ;
536574
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) {
546577 ret = UseWhileBorrowed ( loan. loan_path . clone ( ) , loan. span ) ;
547578 false
548579 } else {
549580 true
550581 }
551582 } ) ;
552583
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-
587584 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- }
594585 }
595586
596587 fn check_if_path_is_moved ( & self ,
@@ -668,11 +659,9 @@ impl<'a> CheckLoanCtxt<'a> {
668659 // and aliasing restrictions:
669660 if assignee_cmt. mutbl . is_mutable ( ) {
670661 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 ( ) ) ;
676665 mark_variable_as_used_mut ( self , assignee_cmt) ;
677666 }
678667 }
@@ -807,138 +796,24 @@ impl<'a> CheckLoanCtxt<'a> {
807796 }
808797 }
809798
810- fn check_for_assignment_to_restricted_or_frozen_location (
799+ fn check_for_assignment_to_borrowed_path (
811800 this : & CheckLoanCtxt ,
812801 assignment_id : ast:: NodeId ,
813802 assignment_span : Span ,
814- assignee_cmt : mc:: cmt ) -> bool
803+ assignee_cmt : mc:: cmt )
815804 {
816805 //! Check for assignments that violate the terms of an
817806 //! outstanding loan.
818807
819808 let loan_path = match opt_loan_path ( & assignee_cmt) {
820809 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 */ }
822811 } ;
823812
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
863816 } ) ;
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- }
942817 }
943818 }
944819
0 commit comments