Skip to content

Commit f0e0d9e

Browse files
committed
auto merge of #12117 : nikomatsakis/rust/issue-11913-borrow-in-aliasable-loc, r=pcwalton
Repair a rather embarassingly obvious hole that I created as part of #9629. In particular, prevent `&mut` borrows of data in an aliasable location. This used to be prevented through the restrictions mechanism, but in #9629 I modified those rules incorrectly. r? @pcwalton Fixes #11913
2 parents 49ac48d + eb774f6 commit f0e0d9e

23 files changed

+317
-264
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

+16-44
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,9 +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,
225-
ConstMutability => RESTR_ALIAS,
222+
MutableMutability => RESTR_FREEZE | RESTR_CLAIM,
223+
ImmutableMutability => RESTR_FREEZE,
226224
};
227225
debug!("illegal_if={:?}", illegal_if);
228226

@@ -424,7 +422,7 @@ impl<'a> CheckLoanCtxt<'a> {
424422
debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
425423
cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
426424
match guarantor.cat {
427-
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) => {
425+
mc::cat_deref(b, _, mc::region_ptr(ast::MutMutable, _)) => {
428426
// Statically prohibit writes to `&mut` when aliasable
429427

430428
check_for_aliasability_violation(this, expr, b);
@@ -438,43 +436,18 @@ impl<'a> CheckLoanCtxt<'a> {
438436

439437
fn check_for_aliasability_violation(this: &CheckLoanCtxt,
440438
expr: &ast::Expr,
441-
cmt: mc::cmt) -> bool {
442-
let mut cmt = cmt;
443-
444-
loop {
445-
match cmt.cat {
446-
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) |
447-
mc::cat_downcast(b) |
448-
mc::cat_stack_upvar(b) |
449-
mc::cat_deref(b, _, mc::uniq_ptr) |
450-
mc::cat_interior(b, _) |
451-
mc::cat_discr(b, _) => {
452-
// Aliasability depends on base cmt
453-
cmt = b;
454-
}
455-
456-
mc::cat_copied_upvar(_) |
457-
mc::cat_rvalue(..) |
458-
mc::cat_local(..) |
459-
mc::cat_arg(_) |
460-
mc::cat_deref(_, _, mc::unsafe_ptr(..)) |
461-
mc::cat_static_item(..) |
462-
mc::cat_deref(_, _, mc::gc_ptr) |
463-
mc::cat_deref(_, _, mc::region_ptr(MutImmutable, _)) => {
464-
// Aliasability is independent of base cmt
465-
match cmt.freely_aliasable() {
466-
None => {
467-
return true;
468-
}
469-
Some(cause) => {
470-
this.bccx.report_aliasability_violation(
471-
expr.span,
472-
MutabilityViolation,
473-
cause);
474-
return false;
475-
}
476-
}
477-
}
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;
478451
}
479452
}
480453
}
@@ -598,8 +571,7 @@ impl<'a> CheckLoanCtxt<'a> {
598571

599572
// Check for a non-const loan of `loan_path`
600573
let cont = this.each_in_scope_loan(expr.id, |loan| {
601-
if loan.loan_path == loan_path &&
602-
loan.mutbl != ConstMutability {
574+
if loan.loan_path == loan_path {
603575
this.report_illegal_mutation(expr,
604576
full_loan_path,
605577
loan);

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-10
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(
@@ -568,11 +573,6 @@ impl<'a> GatherLoanCtxt<'a> {
568573
//! Implements the M-* rules in doc.rs.
569574
570575
match req_mutbl {
571-
ConstMutability => {
572-
// Data of any mutability can be lent as const.
573-
Ok(())
574-
}
575-
576576
ImmutableMutability => {
577577
// both imm and mut data can be lent as imm;
578578
// for mutable data, this is a freeze
@@ -591,16 +591,53 @@ impl<'a> GatherLoanCtxt<'a> {
591591
}
592592
}
593593
}
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+
}
594634
}
595635

596636
pub fn restriction_set(&self, req_mutbl: LoanMutability)
597637
-> RestrictionSet {
598638
match req_mutbl {
599-
ConstMutability => RESTR_EMPTY,
600-
ImmutableMutability => RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM,
601-
MutableMutability => {
602-
RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE
603-
}
639+
ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM,
640+
MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE,
604641
}
605642
}
606643

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
}

0 commit comments

Comments
 (0)