Skip to content

Commit 79fca99

Browse files
committed
auto merge of #14947 : zwarich/rust/check-loans-not-restrictions, r=nikomatsakis
Now that features like `const` are gone, we can remove the concept of restrictions from borrowck and just track loans and their restricted paths.
2 parents 0996766 + 480cd6f commit 79fca99

File tree

4 files changed

+107
-324
lines changed

4 files changed

+107
-324
lines changed

src/librustc/middle/borrowck/check_loans.rs

+82-207
Original file line numberDiff line numberDiff line change
@@ -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+
158164
impl<'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

Comments
 (0)