Skip to content

Commit e9b9067

Browse files
committed
Make &mut borrows restrict aliasing
Fixes #11913
1 parent 3df1eb2 commit e9b9067

File tree

8 files changed

+159
-121
lines changed

8 files changed

+159
-121
lines changed

src/librustc/metadata/encoder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ fn encode_explicit_self(ebml_w: &mut writer::Encoder, explicit_self: ast::Explic
681681

682682
ebml_w.end_tag();
683683

684-
fn encode_mutability(ebml_w: &writer::Encoder,
684+
fn encode_mutability(ebml_w: &mut writer::Encoder,
685685
m: ast::Mutability) {
686686
match m {
687687
MutImmutable => { ebml_w.writer.write(&[ 'i' as u8 ]); }

src/librustc/middle/borrowck/check_loans.rs

+15-41
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use mc = middle::mem_categorization;
2222
use middle::borrowck::*;
2323
use middle::moves;
2424
use middle::ty;
25-
use syntax::ast::{MutImmutable, MutMutable};
2625
use syntax::ast;
2726
use syntax::ast_map;
2827
use syntax::ast_util;
@@ -220,8 +219,8 @@ impl<'a> CheckLoanCtxt<'a> {
220219

221220
// Restrictions that would cause the new loan to be illegal:
222221
let illegal_if = match loan2.mutbl {
223-
MutableMutability => RESTR_ALIAS | RESTR_FREEZE | RESTR_CLAIM,
224-
ImmutableMutability => RESTR_ALIAS | RESTR_FREEZE,
222+
MutableMutability => RESTR_FREEZE | RESTR_CLAIM,
223+
ImmutableMutability => RESTR_FREEZE,
225224
};
226225
debug!("illegal_if={:?}", illegal_if);
227226

@@ -423,7 +422,7 @@ impl<'a> CheckLoanCtxt<'a> {
423422
debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
424423
cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
425424
match guarantor.cat {
426-
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) => {
425+
mc::cat_deref(b, _, mc::region_ptr(ast::MutMutable, _)) => {
427426
// Statically prohibit writes to `&mut` when aliasable
428427

429428
check_for_aliasability_violation(this, expr, b);
@@ -437,43 +436,18 @@ impl<'a> CheckLoanCtxt<'a> {
437436

438437
fn check_for_aliasability_violation(this: &CheckLoanCtxt,
439438
expr: &ast::Expr,
440-
cmt: mc::cmt) -> bool {
441-
let mut cmt = cmt;
442-
443-
loop {
444-
match cmt.cat {
445-
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) |
446-
mc::cat_downcast(b) |
447-
mc::cat_stack_upvar(b) |
448-
mc::cat_deref(b, _, mc::uniq_ptr) |
449-
mc::cat_interior(b, _) |
450-
mc::cat_discr(b, _) => {
451-
// Aliasability depends on base cmt
452-
cmt = b;
453-
}
454-
455-
mc::cat_copied_upvar(_) |
456-
mc::cat_rvalue(..) |
457-
mc::cat_local(..) |
458-
mc::cat_arg(_) |
459-
mc::cat_deref(_, _, mc::unsafe_ptr(..)) |
460-
mc::cat_static_item(..) |
461-
mc::cat_deref(_, _, mc::gc_ptr) |
462-
mc::cat_deref(_, _, mc::region_ptr(MutImmutable, _)) => {
463-
// Aliasability is independent of base cmt
464-
match cmt.freely_aliasable() {
465-
None => {
466-
return true;
467-
}
468-
Some(cause) => {
469-
this.bccx.report_aliasability_violation(
470-
expr.span,
471-
MutabilityViolation,
472-
cause);
473-
return false;
474-
}
475-
}
476-
}
439+
cmt: mc::cmt)
440+
-> bool {
441+
match cmt.freely_aliasable() {
442+
None => {
443+
return true;
444+
}
445+
Some(cause) => {
446+
this.bccx.report_aliasability_violation(
447+
expr.span,
448+
MutabilityViolation,
449+
cause);
450+
return false;
477451
}
478452
}
479453
}

src/librustc/middle/borrowck/doc.rs

+55-13
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,13 @@ that the value `(*x).f` may be mutated via the newly created reference
151151
restrictions `RS` that accompany the loan.
152152
153153
The first restriction `((*x).f, [MUTATE, CLAIM, FREEZE])` states that
154-
the lender may not mutate nor freeze `(*x).f`. Mutation is illegal
155-
because `(*x).f` is only supposed to be mutated via the new reference,
156-
not by mutating the original path `(*x).f`. Freezing is
154+
the lender may not mutate, freeze, nor alias `(*x).f`. Mutation is
155+
illegal because `(*x).f` is only supposed to be mutated via the new
156+
reference, not by mutating the original path `(*x).f`. Freezing is
157157
illegal because the path now has an `&mut` alias; so even if we the
158158
lender were to consider `(*x).f` to be immutable, it might be mutated
159-
via this alias. Both of these restrictions are temporary. They will be
160-
enforced for the lifetime `'a` of the loan. After the loan expires,
161-
the restrictions no longer apply.
159+
via this alias. They will be enforced for the lifetime `'a` of the
160+
loan. After the loan expires, the restrictions no longer apply.
162161
163162
The second restriction on `*x` is interesting because it does not
164163
apply to the path that was lent (`(*x).f`) but rather to a prefix of
@@ -188,11 +187,9 @@ The kinds of expressions which in-scope loans can render illegal are:
188187
against mutating `lv`;
189188
- *moves*: illegal if there is any in-scope restriction on `lv` at all;
190189
- *mutable borrows* (`&mut lv`): illegal there is an in-scope restriction
191-
against mutating `lv` or aliasing `lv`;
190+
against claiming `lv`;
192191
- *immutable borrows* (`&lv`): illegal there is an in-scope restriction
193-
against freezing `lv` or aliasing `lv`;
194-
- *read-only borrows* (`&const lv`): illegal there is an in-scope restriction
195-
against aliasing `lv`.
192+
against freezing `lv`.
196193
197194
## Formal rules
198195
@@ -238,19 +235,23 @@ live. (This is done via restrictions, read on.)
238235
We start with the `gather_loans` pass, which walks the AST looking for
239236
borrows. For each borrow, there are three bits of information: the
240237
lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT`
241-
of the resulting pointer. Given those, `gather_loans` applies three
238+
of the resulting pointer. Given those, `gather_loans` applies four
242239
validity tests:
243240
244241
1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
245242
compatible with the mutability of `LV` (i.e., not borrowing immutable
246243
data as mutable).
247244
248-
2. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
245+
2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
246+
compatible with the aliasability of `LV`. The goal is to prevent
247+
`&mut` borrows of aliasability data.
248+
249+
3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
249250
the lifetime of the value being borrowed. This pass is also
250251
responsible for inserting root annotations to keep managed values
251252
alive.
252253
253-
3. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
254+
4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
254255
restrictions to maintain memory safety. These are the restrictions
255256
that will go into the final loan. We'll discuss in more detail below.
256257
@@ -313,6 +314,47 @@ be borrowed if MQ is immutable or const:
313314
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
314315
TYPE(LV) = &mut Ty
315316
317+
## Checking aliasability
318+
319+
The goal of the aliasability check is to ensure that we never permit
320+
`&mut` borrows of aliasable data. Formally we define a predicate
321+
`ALIASABLE(LV, MQ)` which if defined means that
322+
"borrowing `LV` with mutability `MQ` is ok". The
323+
Rust code corresponding to this predicate is the function
324+
`check_aliasability()` in `middle::borrowck::gather_loans`.
325+
326+
### Checking aliasability of variables
327+
328+
Local variables are never aliasable as they are accessible only within
329+
the stack frame.
330+
331+
ALIASABLE(X, MQ) // M-Var-Mut
332+
333+
### Checking aliasable of owned content
334+
335+
Owned content is aliasable if it is found in an aliasable location:
336+
337+
ALIASABLE(LV.f, MQ) // M-Field
338+
ALIASABLE(LV, MQ)
339+
340+
ALIASABLE(*LV, MQ) // M-Deref-Unique
341+
ALIASABLE(LV, MQ)
342+
343+
### Checking mutability of immutable pointer types
344+
345+
Immutable pointer types like `&T` are aliasable, and hence can only be
346+
borrowed immutably:
347+
348+
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
349+
TYPE(LV) = &Ty
350+
351+
### Checking mutability of mutable pointer types
352+
353+
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
354+
355+
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
356+
TYPE(LV) = &mut Ty
357+
316358
## Checking lifetime
317359
318360
These rules aim to ensure that no data is borrowed for a scope that exceeds

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

+47-4
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,11 @@ impl<'a> GatherLoanCtxt<'a> {
460460
return; // reported an error, no sense in reporting more.
461461
}
462462

463+
// Check that we don't allow mutable borrows of aliasable data.
464+
if check_aliasability(self.bccx, borrow_span, cmt, req_mutbl).is_err() {
465+
return; // reported an error, no sense in reporting more.
466+
}
467+
463468
// Compute the restrictions that are required to enforce the
464469
// loan is safe.
465470
let restr = restrictions::compute_restrictions(
@@ -586,15 +591,53 @@ impl<'a> GatherLoanCtxt<'a> {
586591
}
587592
}
588593
}
594+
595+
fn check_aliasability(bccx: &BorrowckCtxt,
596+
borrow_span: Span,
597+
cmt: mc::cmt,
598+
req_mutbl: LoanMutability) -> Result<(),()> {
599+
//! Implements the A-* rules in doc.rs.
600+
601+
match req_mutbl {
602+
ImmutableMutability => {
603+
// both imm and mut data can be lent as imm;
604+
// for mutable data, this is a freeze
605+
Ok(())
606+
}
607+
608+
MutableMutability => {
609+
// Check for those cases where we cannot control
610+
// the aliasing and make sure that we are not
611+
// being asked to.
612+
match cmt.freely_aliasable() {
613+
None => {
614+
Ok(())
615+
}
616+
Some(mc::AliasableStaticMut) => {
617+
// This is nasty, but we ignore the
618+
// aliasing rules if the data is based in
619+
// a `static mut`, since those are always
620+
// unsafe. At your own peril and all that.
621+
Ok(())
622+
}
623+
Some(cause) => {
624+
bccx.report_aliasability_violation(
625+
borrow_span,
626+
BorrowViolation,
627+
cause);
628+
Err(())
629+
}
630+
}
631+
}
632+
}
633+
}
589634
}
590635

591636
pub fn restriction_set(&self, req_mutbl: LoanMutability)
592637
-> RestrictionSet {
593638
match req_mutbl {
594-
ImmutableMutability => RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM,
595-
MutableMutability => {
596-
RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE
597-
}
639+
ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM,
640+
MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE,
598641
}
599642
}
600643

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

-34
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,6 @@ impl<'a> RestrictionsContext<'a> {
5353
fn restrict(&self,
5454
cmt: mc::cmt,
5555
restrictions: RestrictionSet) -> RestrictionResult {
56-
57-
// Check for those cases where we cannot control the aliasing
58-
// and make sure that we are not being asked to.
59-
match cmt.freely_aliasable() {
60-
None => {}
61-
Some(cause) => {
62-
self.check_aliasing_permitted(cause, restrictions);
63-
}
64-
}
65-
6656
match cmt.cat {
6757
mc::cat_rvalue(..) => {
6858
// Effectively, rvalues are stored into a
@@ -179,28 +169,4 @@ impl<'a> RestrictionsContext<'a> {
179169
}
180170
}
181171
}
182-
183-
fn check_aliasing_permitted(&self,
184-
cause: mc::AliasableReason,
185-
restrictions: RestrictionSet) {
186-
//! This method is invoked when the current `cmt` is something
187-
//! where aliasing cannot be controlled. It reports an error if
188-
//! the restrictions required that it not be aliased; currently
189-
//! this only occurs when re-borrowing an `&mut` pointer.
190-
//!
191-
//! NB: To be 100% consistent, we should report an error if
192-
//! RESTR_FREEZE is found, because we cannot prevent freezing,
193-
//! nor would we want to. However, we do not report such an
194-
//! error, because this restriction only occurs when the user
195-
//! is creating an `&mut` pointer to immutable or read-only
196-
//! data, and there is already another piece of code that
197-
//! checks for this condition.
198-
199-
if restrictions.intersects(RESTR_ALIAS) {
200-
self.bccx.report_aliasability_violation(
201-
self.span,
202-
BorrowViolation,
203-
cause);
204-
}
205-
}
206172
}

src/librustc/middle/borrowck/mod.rs

+13-11
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,6 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
316316
// - `RESTR_MUTATE`: The lvalue may not be modified.
317317
// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden.
318318
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
319-
// - `RESTR_ALIAS`: All borrows of the lvalue are forbidden.
320319
//
321320
// In addition, no value which is restricted may be moved. Therefore,
322321
// restrictions are meaningful even if the RestrictionSet is empty,
@@ -336,7 +335,6 @@ pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000};
336335
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
337336
pub static RESTR_CLAIM: RestrictionSet = RestrictionSet {bits: 0b0010};
338337
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100};
339-
pub static RESTR_ALIAS: RestrictionSet = RestrictionSet {bits: 0b1000};
340338

341339
impl RestrictionSet {
342340
pub fn intersects(&self, restr: RestrictionSet) -> bool {
@@ -661,8 +659,8 @@ impl BorrowckCtxt {
661659
kind: AliasableViolationKind,
662660
cause: mc::AliasableReason) {
663661
let prefix = match kind {
664-
MutabilityViolation => "cannot assign to an `&mut`",
665-
BorrowViolation => "cannot borrow an `&mut`"
662+
MutabilityViolation => "cannot assign to data",
663+
BorrowViolation => "cannot borrow data mutably"
666664
};
667665

668666
match cause {
@@ -671,17 +669,21 @@ impl BorrowckCtxt {
671669
span,
672670
format!("{} in an aliasable location", prefix));
673671
}
672+
mc::AliasableStatic |
673+
mc::AliasableStaticMut => {
674+
self.tcx.sess.span_err(
675+
span,
676+
format!("{} in a static location", prefix));
677+
}
674678
mc::AliasableManaged => {
675-
self.tcx.sess.span_err(span, format!("{} in a `@` pointer",
676-
prefix))
679+
self.tcx.sess.span_err(
680+
span,
681+
format!("{} in a `@` pointer", prefix));
677682
}
678-
mc::AliasableBorrowed(m) => {
683+
mc::AliasableBorrowed(_) => {
679684
self.tcx.sess.span_err(
680685
span,
681-
format!("{} in a `&{}` pointer; \
682-
try an `&mut` instead",
683-
prefix,
684-
self.mut_to_keyword(m)));
686+
format!("{} in a `&` reference", prefix));
685687
}
686688
}
687689
}

0 commit comments

Comments
 (0)