Skip to content

Commit bf1647c

Browse files
committed
Reconcile docs and code, adding examples and adding RESTR_CLAIM
1 parent 329f7a1 commit bf1647c

9 files changed

+165
-33
lines changed

src/librustc/middle/borrowck/check_loans.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub fn check_loans(bccx: @BorrowckCtxt,
6565

6666
enum MoveError {
6767
MoveOk,
68-
MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
68+
MoveWhileBorrowed(/*move*/@LoanPath, /*loan*/@LoanPath, /*loan*/span)
6969
}
7070

7171
pub impl<'self> CheckLoanCtxt<'self> {
@@ -200,9 +200,9 @@ pub impl<'self> CheckLoanCtxt<'self> {
200200
loan1.repr(self.tcx()),
201201
loan2.repr(self.tcx()));
202202

203-
// Restrictions that would cause the new loan to be immutable:
203+
// Restrictions that would cause the new loan to be illegal:
204204
let illegal_if = match loan2.mutbl {
205-
m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_MUTATE,
205+
m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_CLAIM,
206206
m_imm => RESTR_ALIAS | RESTR_FREEZE,
207207
m_const => RESTR_ALIAS,
208208
};
@@ -557,12 +557,12 @@ pub impl<'self> CheckLoanCtxt<'self> {
557557
let cmt = self.bccx.cat_expr(ex);
558558
match self.analyze_move_out_from_cmt(cmt) {
559559
MoveOk => {}
560-
MoveWhileBorrowed(loan_path, loan_span) => {
560+
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
561561
self.bccx.span_err(
562562
cmt.span,
563563
fmt!("cannot move out of `%s` \
564564
because it is borrowed",
565-
self.bccx.loan_path_to_str(loan_path)));
565+
self.bccx.loan_path_to_str(move_path)));
566566
self.bccx.span_note(
567567
loan_span,
568568
fmt!("borrow of `%s` occurs here",
@@ -582,7 +582,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
582582
for opt_loan_path(cmt).each |&lp| {
583583
for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
584584
// Any restriction prevents moves.
585-
return MoveWhileBorrowed(loan.loan_path, loan.span);
585+
return MoveWhileBorrowed(lp, loan.loan_path, loan.span);
586586
}
587587
}
588588

src/librustc/middle/borrowck/doc.rs

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -580,15 +580,27 @@ borrow, `LV` remains the *sole pointer with mutable access* to `*LV`.
580580
Restrictions against mutations and claims are necessary because if the
581581
pointer in `LV` were to be somehow copied or moved to a different
582582
location, then the restriction issued for `*LV` would not apply to the
583-
new location. Consider this example, where `*t0` is frozen, but then
584-
`t0` and `t1` are swapped, so by mutating `*t1` the user can mutate
585-
the frozen memory that was originally found at `*t0`:
586-
587-
fn foo(t0: &mut T,
588-
t1: &mut T) {
589-
let p: &T = &*t0; // Freezes `*t0`
590-
t0 <-> t1;
591-
*t1 = ...; // Invalidates `p`
583+
new location. Note that because `&mut` values are non-copyable, a
584+
simple attempt to move the base pointer will fail due to the
585+
(implicit) restriction against moves:
586+
587+
// src/test/compile-fail/borrowck-move-mut-base-ptr.rs
588+
fn foo(t0: &mut int) {
589+
let p: &int = &*t0; // Freezes `*t0`
590+
let t1 = t0; //~ ERROR cannot move out of `t0`
591+
*t1 = 22;
592+
}
593+
594+
However, the additional restrictions against mutation mean that even a
595+
clever attempt to use a swap to circumvent the type system will
596+
encounter an error:
597+
598+
// src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
599+
fn foo<'a>(mut t0: &'a mut int,
600+
mut t1: &'a mut int) {
601+
let p: &int = &*t0; // Freezes `*t0`
602+
swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0`
603+
*t1 = 22;
592604
}
593605
594606
The restriction against *aliasing* (and, in turn, freezing) is
@@ -598,12 +610,32 @@ pointee. Since we are only issuing restrictions against `*LV`, these
598610
other aliases would be unrestricted, and the result would be
599611
unsound. For example:
600612
601-
fn foo(t0: &mut T) {
602-
let p: &T = &*t0; // Freezes *t0
603-
let q: &&mut T = &t0;
604-
**q = ...; // Invalidates `p`
613+
// src/test/compile-fail/borrowck-alias-mut-base-ptr.rs
614+
fn foo(t0: &mut int) {
615+
let p: &int = &*t0; // Freezes `*t0`
616+
let q: &const &mut int = &const t0; //~ ERROR cannot borrow `t0`
617+
**q = 22; // (*)
605618
}
606619
620+
Note that the current rules also report an error at the assignment in
621+
`(*)`, because we only permit `&mut` poiners to be assigned if they
622+
are located in a non-aliasable location. However, I do not believe
623+
this restriction is strictly necessary. It was added, I believe, to
624+
discourage `&mut` from being placed in aliasable locations in the
625+
first place. One (desirable) side-effect of restricting aliasing on
626+
`LV` is that borrowing an `&mut` pointee found inside an aliasable
627+
pointee yields an error:
628+
629+
// src/test/compile-fail/borrowck-borrow-mut-base-ptr-in-aliasable-loc:
630+
fn foo(t0: & &mut int) {
631+
let t1 = t0;
632+
let p: &int = &**t0; //~ ERROR cannot borrow an `&mut` in a `&` pointer
633+
**t1 = 22; // (*)
634+
}
635+
636+
Here at the line `(*)` you will also see the error I referred to
637+
above, which I do not believe is strictly necessary.
638+
607639
The second rule for `&mut` handles the case where we are not adding
608640
any restrictions (beyond the default of "no move"):
609641
@@ -622,4 +654,22 @@ that way if we *can* find a simple static error, we will:
622654
RESTRICTIONS(*LV, ACTIONS) = [*LV, ACTIONS] // R-Deref-Managed-Borrowed
623655
TYPE(LV) = @mut Ty
624656
657+
# Some notes for future work
658+
659+
While writing up these docs, I encountered some rules I believe to be
660+
stricter than necessary:
661+
662+
- I think the restriction against mutating `&mut` pointers found in an
663+
aliasable location is unnecessary. They cannot be reborrowed, to be sure,
664+
so it should be safe to mutate them. Lifting this might cause some common
665+
cases (`&mut int`) to work just fine, but might lead to further confusion
666+
in other cases, so maybe it's best to leave it as is.
667+
- I think restricting the `&mut` LV against moves and `ALIAS` is sufficient,
668+
`MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was
669+
a built-in operator, but as it is not, it is implied by `CLAIM`,
670+
and `CLAIM` is implied by `ALIAS`. The only net effect of this is an
671+
extra error message in some cases, though.
672+
- I have not described how closures interact. Current code is unsound.
673+
I am working on describing and implementing the fix.
674+
625675
*/

src/librustc/middle/borrowck/gather_loans/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,8 @@ pub impl GatherLoanCtxt {
517517
fn restriction_set(&self, req_mutbl: ast::mutability) -> RestrictionSet {
518518
match req_mutbl {
519519
m_const => RESTR_EMPTY,
520-
m_imm => RESTR_EMPTY | RESTR_MUTATE,
521-
m_mutbl => RESTR_EMPTY | RESTR_MUTATE | RESTR_FREEZE
520+
m_imm => RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM,
521+
m_mutbl => RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE
522522
}
523523
}
524524

src/librustc/middle/borrowck/gather_loans/restrictions.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl RestrictionsContext {
8686
// When we borrow the interior of an enum, we have to
8787
// ensure the enum itself is not mutated, because that
8888
// could cause the type of the memory to change.
89-
self.compute(cmt_base, restrictions | RESTR_MUTATE)
89+
self.compute(cmt_base, restrictions | RESTR_MUTATE | RESTR_CLAIM)
9090
}
9191

9292
mc::cat_interior(cmt_base, i) => {
@@ -105,7 +105,9 @@ impl RestrictionsContext {
105105
// When we borrow the interior of an owned pointer, we
106106
// cannot permit the base to be mutated, because that
107107
// would cause the unique pointer to be freed.
108-
let result = self.compute(cmt_base, restrictions | RESTR_MUTATE);
108+
let result = self.compute(
109+
cmt_base,
110+
restrictions | RESTR_MUTATE | RESTR_CLAIM);
109111
self.extend(result, cmt.mutbl, LpDeref, restrictions)
110112
}
111113

@@ -178,7 +180,9 @@ impl RestrictionsContext {
178180
// mutability, we can only prevent mutation or prevent
179181
// freezing if it is not aliased. Therefore, in such
180182
// cases we restrict aliasing on `cmt_base`.
181-
if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) {
183+
if restrictions.intersects(RESTR_MUTATE |
184+
RESTR_CLAIM |
185+
RESTR_FREEZE) {
182186
// R-Deref-Mut-Borrowed-1
183187
let result = self.compute(cmt_base, restrictions | RESTR_ALIAS);
184188
self.extend(result, cmt.mutbl, LpDeref, restrictions)
@@ -244,7 +248,7 @@ impl RestrictionsContext {
244248
fn check_no_mutability_control(&self,
245249
cmt: mc::cmt,
246250
restrictions: RestrictionSet) {
247-
if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) {
251+
if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE | RESTR_CLAIM) {
248252
self.bccx.report(BckError {span: self.span,
249253
cmt: cmt,
250254
code: err_freeze_aliasable_const});

src/librustc/middle/borrowck/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,10 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
301301
// Borrowing an lvalue often results in *restrictions* that limit what
302302
// can be done with this lvalue during the scope of the loan:
303303
//
304-
// - `RESTR_MUTATE`: The lvalue may not be modified and mutable pointers to
305-
// the value cannot be created.
306-
// - `RESTR_FREEZE`: Immutable pointers to the value cannot be created.
307-
// - `RESTR_ALIAS`: The lvalue may not be aliased in any way.
304+
// - `RESTR_MUTATE`: The lvalue may not be modified.
305+
// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden.
306+
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
307+
// - `RESTR_ALIAS`: All borrows of the lvalue are forbidden.
308308
//
309309
// In addition, no value which is restricted may be moved. Therefore,
310310
// restrictions are meaningful even if the RestrictionSet is empty,
@@ -319,10 +319,11 @@ pub struct RestrictionSet {
319319
bits: u32
320320
}
321321

322-
pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b000};
323-
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b001};
324-
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b010};
325-
pub static RESTR_ALIAS: RestrictionSet = RestrictionSet {bits: 0b100};
322+
pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000};
323+
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
324+
pub static RESTR_CLAIM: RestrictionSet = RestrictionSet {bits: 0b0010};
325+
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100};
326+
pub static RESTR_ALIAS: RestrictionSet = RestrictionSet {bits: 0b1000};
326327

327328
pub impl RestrictionSet {
328329
fn intersects(&self, restr: RestrictionSet) -> bool {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Test that attempt to alias `&mut` pointer while pointee is borrowed
2+
// yields an error.
3+
//
4+
// Example from src/middle/borrowck/doc.rs
5+
6+
use std::util::swap;
7+
8+
fn foo(t0: &mut int) {
9+
let p: &int = &*t0; // Freezes `*t0`
10+
let q: &const &mut int = &const t0; //~ ERROR cannot borrow `t0`
11+
**q = 22; //~ ERROR cannot assign to an `&mut` in a `&const` pointer
12+
}
13+
14+
fn main() {
15+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Test that attempt to reborrow an `&mut` pointer in an aliasable
2+
// location yields an error.
3+
//
4+
// Example from src/middle/borrowck/doc.rs
5+
6+
use std::util::swap;
7+
8+
fn foo(t0: & &mut int) {
9+
let t1 = t0;
10+
let p: &int = &**t0; //~ ERROR cannot borrow an `&mut` in a `&` pointer
11+
**t1 = 22; //~ ERROR cannot assign
12+
}
13+
14+
fn foo2(t0: &const &mut int) {
15+
// Note: reborrowing from an &const actually yields two errors, since it
16+
// is unsafe in two ways: we can't control the aliasing, and we can't
17+
// control the mutation.
18+
let t1 = t0;
19+
let p: &int = &**t0; //~ ERROR cannot borrow an `&mut` in a `&const` pointer
20+
//~^ ERROR unsafe borrow of aliasable, const value
21+
**t1 = 22; //~ ERROR cannot assign
22+
}
23+
24+
fn foo3(t0: &mut &mut int) {
25+
let t1 = &mut *t0;
26+
let p: &int = &**t0; //~ ERROR cannot borrow
27+
**t1 = 22;
28+
}
29+
30+
fn main() {
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Test that attempt to move `&mut` pointer while pointee is borrowed
2+
// yields an error.
3+
//
4+
// Example from src/middle/borrowck/doc.rs
5+
6+
use std::util::swap;
7+
8+
fn foo(t0: &mut int) {
9+
let p: &int = &*t0; // Freezes `*t0`
10+
let t1 = t0; //~ ERROR cannot move out of `t0`
11+
*t1 = 22;
12+
}
13+
14+
fn main() {
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Test that attempt to swap `&mut` pointer while pointee is borrowed
2+
// yields an error.
3+
//
4+
// Example from src/middle/borrowck/doc.rs
5+
6+
use std::util::swap;
7+
8+
fn foo<'a>(mut t0: &'a mut int,
9+
mut t1: &'a mut int) {
10+
let p: &int = &*t0; // Freezes `*t0`
11+
swap(&mut t0, &mut t1); //~ ERROR cannot borrow `t0`
12+
*t1 = 22;
13+
}
14+
15+
fn main() {
16+
}

0 commit comments

Comments
 (0)