Skip to content

Issue 11913 borrow in aliasable loc #12117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ fn encode_explicit_self(ebml_w: &mut writer::Encoder, explicit_self: ast::Explic

ebml_w.end_tag();

fn encode_mutability(ebml_w: &writer::Encoder,
fn encode_mutability(ebml_w: &mut writer::Encoder,
m: ast::Mutability) {
match m {
MutImmutable => { ebml_w.writer.write(&[ 'i' as u8 ]); }
Expand Down
60 changes: 16 additions & 44 deletions src/librustc/middle/borrowck/check_loans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use mc = middle::mem_categorization;
use middle::borrowck::*;
use middle::moves;
use middle::ty;
use syntax::ast::{MutImmutable, MutMutable};
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util;
Expand Down Expand Up @@ -220,9 +219,8 @@ impl<'a> CheckLoanCtxt<'a> {

// Restrictions that would cause the new loan to be illegal:
let illegal_if = match loan2.mutbl {
MutableMutability => RESTR_ALIAS | RESTR_FREEZE | RESTR_CLAIM,
ImmutableMutability => RESTR_ALIAS | RESTR_FREEZE,
ConstMutability => RESTR_ALIAS,
MutableMutability => RESTR_FREEZE | RESTR_CLAIM,
ImmutableMutability => RESTR_FREEZE,
};
debug!("illegal_if={:?}", illegal_if);

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

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

fn check_for_aliasability_violation(this: &CheckLoanCtxt,
expr: &ast::Expr,
cmt: mc::cmt) -> bool {
let mut cmt = cmt;

loop {
match cmt.cat {
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) |
mc::cat_downcast(b) |
mc::cat_stack_upvar(b) |
mc::cat_deref(b, _, mc::uniq_ptr) |
mc::cat_interior(b, _) |
mc::cat_discr(b, _) => {
// Aliasability depends on base cmt
cmt = b;
}

mc::cat_copied_upvar(_) |
mc::cat_rvalue(..) |
mc::cat_local(..) |
mc::cat_arg(_) |
mc::cat_deref(_, _, mc::unsafe_ptr(..)) |
mc::cat_static_item(..) |
mc::cat_deref(_, _, mc::gc_ptr) |
mc::cat_deref(_, _, mc::region_ptr(MutImmutable, _)) => {
// Aliasability is independent of base cmt
match cmt.freely_aliasable() {
None => {
return true;
}
Some(cause) => {
this.bccx.report_aliasability_violation(
expr.span,
MutabilityViolation,
cause);
return false;
}
}
}
cmt: mc::cmt)
-> bool {
match cmt.freely_aliasable() {
None => {
return true;
}
Some(cause) => {
this.bccx.report_aliasability_violation(
expr.span,
MutabilityViolation,
cause);
return false;
}
}
}
Expand Down Expand Up @@ -598,8 +571,7 @@ impl<'a> CheckLoanCtxt<'a> {

// Check for a non-const loan of `loan_path`
let cont = this.each_in_scope_loan(expr.id, |loan| {
if loan.loan_path == loan_path &&
loan.mutbl != ConstMutability {
if loan.loan_path == loan_path {
this.report_illegal_mutation(expr,
full_loan_path,
loan);
Expand Down
68 changes: 55 additions & 13 deletions src/librustc/middle/borrowck/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,13 @@ that the value `(*x).f` may be mutated via the newly created reference
restrictions `RS` that accompany the loan.

The first restriction `((*x).f, [MUTATE, CLAIM, FREEZE])` states that
the lender may not mutate nor freeze `(*x).f`. Mutation is illegal
because `(*x).f` is only supposed to be mutated via the new reference,
not by mutating the original path `(*x).f`. Freezing is
the lender may not mutate, freeze, nor alias `(*x).f`. Mutation is
illegal because `(*x).f` is only supposed to be mutated via the new
reference, not by mutating the original path `(*x).f`. Freezing is
illegal because the path now has an `&mut` alias; so even if we the
lender were to consider `(*x).f` to be immutable, it might be mutated
via this alias. Both of these restrictions are temporary. They will be
enforced for the lifetime `'a` of the loan. After the loan expires,
the restrictions no longer apply.
via this alias. They will be enforced for the lifetime `'a` of the
loan. After the loan expires, the restrictions no longer apply.

The second restriction on `*x` is interesting because it does not
apply to the path that was lent (`(*x).f`) but rather to a prefix of
Expand Down Expand Up @@ -188,11 +187,9 @@ The kinds of expressions which in-scope loans can render illegal are:
against mutating `lv`;
- *moves*: illegal if there is any in-scope restriction on `lv` at all;
- *mutable borrows* (`&mut lv`): illegal there is an in-scope restriction
against mutating `lv` or aliasing `lv`;
against claiming `lv`;
- *immutable borrows* (`&lv`): illegal there is an in-scope restriction
against freezing `lv` or aliasing `lv`;
- *read-only borrows* (`&const lv`): illegal there is an in-scope restriction
against aliasing `lv`.
against freezing `lv`.

## Formal rules

Expand Down Expand Up @@ -238,19 +235,23 @@ live. (This is done via restrictions, read on.)
We start with the `gather_loans` pass, which walks the AST looking for
borrows. For each borrow, there are three bits of information: the
lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT`
of the resulting pointer. Given those, `gather_loans` applies three
of the resulting pointer. Given those, `gather_loans` applies four
validity tests:

1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
compatible with the mutability of `LV` (i.e., not borrowing immutable
data as mutable).

2. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
compatible with the aliasability of `LV`. The goal is to prevent
`&mut` borrows of aliasability data.

3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
the lifetime of the value being borrowed. This pass is also
responsible for inserting root annotations to keep managed values
alive.

3. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
restrictions to maintain memory safety. These are the restrictions
that will go into the final loan. We'll discuss in more detail below.

Expand Down Expand Up @@ -313,6 +314,47 @@ be borrowed if MQ is immutable or const:
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty

## Checking aliasability

The goal of the aliasability check is to ensure that we never permit
`&mut` borrows of aliasable data. Formally we define a predicate
`ALIASABLE(LV, MQ)` which if defined means that
"borrowing `LV` with mutability `MQ` is ok". The
Rust code corresponding to this predicate is the function
`check_aliasability()` in `middle::borrowck::gather_loans`.

### Checking aliasability of variables

Local variables are never aliasable as they are accessible only within
the stack frame.

ALIASABLE(X, MQ) // M-Var-Mut

### Checking aliasable of owned content

Owned content is aliasable if it is found in an aliasable location:

ALIASABLE(LV.f, MQ) // M-Field
ALIASABLE(LV, MQ)

ALIASABLE(*LV, MQ) // M-Deref-Unique
ALIASABLE(LV, MQ)

### Checking mutability of immutable pointer types

Immutable pointer types like `&T` are aliasable, and hence can only be
borrowed immutably:

ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty

### Checking mutability of mutable pointer types

`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:

ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty

## Checking lifetime

These rules aim to ensure that no data is borrowed for a scope that exceeds
Expand Down
57 changes: 47 additions & 10 deletions src/librustc/middle/borrowck/gather_loans/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,11 @@ impl<'a> GatherLoanCtxt<'a> {
return; // reported an error, no sense in reporting more.
}

// Check that we don't allow mutable borrows of aliasable data.
if check_aliasability(self.bccx, borrow_span, cmt, req_mutbl).is_err() {
return; // reported an error, no sense in reporting more.
}

// Compute the restrictions that are required to enforce the
// loan is safe.
let restr = restrictions::compute_restrictions(
Expand Down Expand Up @@ -568,11 +573,6 @@ impl<'a> GatherLoanCtxt<'a> {
//! Implements the M-* rules in doc.rs.

match req_mutbl {
ConstMutability => {
// Data of any mutability can be lent as const.
Ok(())
}

ImmutableMutability => {
// both imm and mut data can be lent as imm;
// for mutable data, this is a freeze
Expand All @@ -591,16 +591,53 @@ impl<'a> GatherLoanCtxt<'a> {
}
}
}

fn check_aliasability(bccx: &BorrowckCtxt,
borrow_span: Span,
cmt: mc::cmt,
req_mutbl: LoanMutability) -> Result<(),()> {
//! Implements the A-* rules in doc.rs.

match req_mutbl {
ImmutableMutability => {
// both imm and mut data can be lent as imm;
// for mutable data, this is a freeze
Ok(())
}

MutableMutability => {
// Check for those cases where we cannot control
// the aliasing and make sure that we are not
// being asked to.
match cmt.freely_aliasable() {
None => {
Ok(())
}
Some(mc::AliasableStaticMut) => {
// This is nasty, but we ignore the
// aliasing rules if the data is based in
// a `static mut`, since those are always
// unsafe. At your own peril and all that.
Ok(())
}
Some(cause) => {
bccx.report_aliasability_violation(
borrow_span,
BorrowViolation,
cause);
Err(())
}
}
}
}
}
}

pub fn restriction_set(&self, req_mutbl: LoanMutability)
-> RestrictionSet {
match req_mutbl {
ConstMutability => RESTR_EMPTY,
ImmutableMutability => RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM,
MutableMutability => {
RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE
}
ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM,
MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE,
}
}

Expand Down
34 changes: 0 additions & 34 deletions src/librustc/middle/borrowck/gather_loans/restrictions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,6 @@ impl<'a> RestrictionsContext<'a> {
fn restrict(&self,
cmt: mc::cmt,
restrictions: RestrictionSet) -> RestrictionResult {

// Check for those cases where we cannot control the aliasing
// and make sure that we are not being asked to.
match cmt.freely_aliasable() {
None => {}
Some(cause) => {
self.check_aliasing_permitted(cause, restrictions);
}
}

match cmt.cat {
mc::cat_rvalue(..) => {
// Effectively, rvalues are stored into a
Expand Down Expand Up @@ -179,28 +169,4 @@ impl<'a> RestrictionsContext<'a> {
}
}
}

fn check_aliasing_permitted(&self,
cause: mc::AliasableReason,
restrictions: RestrictionSet) {
//! This method is invoked when the current `cmt` is something
//! where aliasing cannot be controlled. It reports an error if
//! the restrictions required that it not be aliased; currently
//! this only occurs when re-borrowing an `&mut` pointer.
//!
//! NB: To be 100% consistent, we should report an error if
//! RESTR_FREEZE is found, because we cannot prevent freezing,
//! nor would we want to. However, we do not report such an
//! error, because this restriction only occurs when the user
//! is creating an `&mut` pointer to immutable or read-only
//! data, and there is already another piece of code that
//! checks for this condition.

if restrictions.intersects(RESTR_ALIAS) {
self.bccx.report_aliasability_violation(
self.span,
BorrowViolation,
cause);
}
}
}
Loading