1
Fork 0

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.
This commit is contained in:
bors 2014-06-17 04:46:26 +00:00
commit 79fca99438
4 changed files with 107 additions and 324 deletions

View file

@ -155,6 +155,12 @@ enum UseError {
UseWhileBorrowed(/*loan*/Rc<LoanPath>, /*loan*/Span) UseWhileBorrowed(/*loan*/Rc<LoanPath>, /*loan*/Span)
} }
fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
borrow_kind2: ty::BorrowKind)
-> bool {
borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
}
impl<'a> CheckLoanCtxt<'a> { impl<'a> CheckLoanCtxt<'a> {
pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx } pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx }
@ -189,29 +195,75 @@ impl<'a> CheckLoanCtxt<'a> {
}) })
} }
pub fn each_in_scope_restriction(&self, fn each_in_scope_loan_affecting_path(&self,
scope_id: ast::NodeId, scope_id: ast::NodeId,
loan_path: &LoanPath, loan_path: &LoanPath,
op: |&Loan, &Restriction| -> bool) op: |&Loan| -> bool)
-> bool { -> bool {
//! Iterates through all the in-scope restrictions for the //! Iterates through all of the in-scope loans affecting `loan_path`,
//! given `loan_path` //! calling `op`, and ceasing iteration if `false` is returned.
self.each_in_scope_loan(scope_id, |loan| { // First, we check for a loan restricting the path P being used. This
debug!("each_in_scope_restriction found loan: {:?}", // accounts for borrows of P but also borrows of subpaths, like P.a.b.
loan.repr(self.tcx())); // Consider the following example:
//
// let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
// let y = a; // Conflicts with restriction
let cont = self.each_in_scope_loan(scope_id, |loan| {
let mut ret = true; let mut ret = true;
for restr in loan.restrictions.iter() { for restr_path in loan.restricted_paths.iter() {
if *restr.loan_path == *loan_path { if **restr_path == *loan_path {
if !op(loan, restr) { if !op(loan) {
ret = false; ret = false;
break; break;
} }
} }
} }
ret ret
}) });
if !cont {
return false;
}
// Next, we must check for *loans* (not restrictions) on the path P or
// any base path. This rejects examples like the following:
//
// let x = &mut a.b;
// let y = a.b.c;
//
// Limiting this search to *loans* and not *restrictions* means that
// examples like the following continue to work:
//
// let x = &mut a.b;
// let y = a.c;
let mut loan_path = loan_path;
loop {
match *loan_path {
LpVar(_) => {
break;
}
LpExtend(ref lp_base, _, _) => {
loan_path = &**lp_base;
}
}
let cont = self.each_in_scope_loan(scope_id, |loan| {
if *loan.loan_path == *loan_path {
op(loan)
} else {
true
}
});
if !cont {
return false;
}
}
return true;
} }
pub fn loans_generated_by(&self, scope_id: ast::NodeId) -> Vec<uint> { pub fn loans_generated_by(&self, scope_id: ast::NodeId) -> Vec<uint> {
@ -288,26 +340,12 @@ impl<'a> CheckLoanCtxt<'a> {
loan1.repr(self.tcx()), loan1.repr(self.tcx()),
loan2.repr(self.tcx())); loan2.repr(self.tcx()));
// Restrictions that would cause the new loan to be illegal: if compatible_borrow_kinds(loan1.kind, loan2.kind) {
let illegal_if = match loan2.kind { return true;
// Look for restrictions against mutation. These are }
// generated by all other borrows.
ty::MutBorrow => RESTR_MUTATE,
// Look for restrictions against freezing (immutable borrows). for restr_path in loan1.restricted_paths.iter() {
// These are generated by `&mut` borrows. if *restr_path != loan2.loan_path { continue; }
ty::ImmBorrow => RESTR_FREEZE,
// No matter how the data is borrowed (as `&`, as `&mut`,
// or as `&unique imm`) it will always generate a
// restriction against mutating the data. So look for those.
ty::UniqueImmBorrow => RESTR_MUTATE,
};
debug!("illegal_if={:?}", illegal_if);
for restr in loan1.restrictions.iter() {
if !restr.set.intersects(illegal_if) { continue; }
if restr.loan_path != loan2.loan_path { continue; }
let old_pronoun = if new_loan.loan_path == old_loan.loan_path { let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
"it".to_string() "it".to_string()
@ -534,15 +572,8 @@ impl<'a> CheckLoanCtxt<'a> {
let mut ret = UseOk; let mut ret = UseOk;
// First, we check for a restriction on the path P being used. This self.each_in_scope_loan_affecting_path(expr_id, use_path, |loan| {
// accounts for borrows of P but also borrows of subpaths, like P.a.b. if !compatible_borrow_kinds(loan.kind, borrow_kind) {
// Consider the following example:
//
// let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
// let y = a; // Conflicts with restriction
self.each_in_scope_restriction(expr_id, use_path, |loan, _restr| {
if incompatible(loan.kind, borrow_kind) {
ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span); ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
false false
} else { } else {
@ -550,47 +581,7 @@ impl<'a> CheckLoanCtxt<'a> {
} }
}); });
// Next, we must check for *loans* (not restrictions) on the path P or
// any base path. This rejects examples like the following:
//
// let x = &mut a.b;
// let y = a.b.c;
//
// Limiting this search to *loans* and not *restrictions* means that
// examples like the following continue to work:
//
// let x = &mut a.b;
// let y = a.c;
let mut loan_path = use_path;
loop {
self.each_in_scope_loan(expr_id, |loan| {
if *loan.loan_path == *loan_path &&
incompatible(loan.kind, borrow_kind) {
ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
false
} else {
true
}
});
match *loan_path {
LpVar(_) => {
break;
}
LpExtend(ref lp_base, _, _) => {
loan_path = &**lp_base;
}
}
}
return ret; return ret;
fn incompatible(borrow_kind1: ty::BorrowKind,
borrow_kind2: ty::BorrowKind)
-> bool {
borrow_kind1 != ty::ImmBorrow || borrow_kind2 != ty::ImmBorrow
}
} }
fn check_if_path_is_moved(&self, fn check_if_path_is_moved(&self,
@ -668,11 +659,9 @@ impl<'a> CheckLoanCtxt<'a> {
// and aliasing restrictions: // and aliasing restrictions:
if assignee_cmt.mutbl.is_mutable() { if assignee_cmt.mutbl.is_mutable() {
if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) { if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
if mode != euv::Init && if mode != euv::Init {
check_for_assignment_to_restricted_or_frozen_location( check_for_assignment_to_borrowed_path(
self, assignment_id, assignment_span, assignee_cmt.clone()) self, assignment_id, assignment_span, assignee_cmt.clone());
{
// Safe, but record for lint pass later:
mark_variable_as_used_mut(self, assignee_cmt); mark_variable_as_used_mut(self, assignee_cmt);
} }
} }
@ -807,138 +796,24 @@ impl<'a> CheckLoanCtxt<'a> {
} }
} }
fn check_for_assignment_to_restricted_or_frozen_location( fn check_for_assignment_to_borrowed_path(
this: &CheckLoanCtxt, this: &CheckLoanCtxt,
assignment_id: ast::NodeId, assignment_id: ast::NodeId,
assignment_span: Span, assignment_span: Span,
assignee_cmt: mc::cmt) -> bool assignee_cmt: mc::cmt)
{ {
//! Check for assignments that violate the terms of an //! Check for assignments that violate the terms of an
//! outstanding loan. //! outstanding loan.
let loan_path = match opt_loan_path(&assignee_cmt) { let loan_path = match opt_loan_path(&assignee_cmt) {
Some(lp) => lp, Some(lp) => lp,
None => { return true; /* no loan path, can't be any loans */ } None => { return; /* no loan path, can't be any loans */ }
}; };
// Start by searching for an assignment to a *restricted* this.each_in_scope_loan_affecting_path(assignment_id, &*loan_path, |loan| {
// location. Here is one example of the kind of error caught this.report_illegal_mutation(assignment_span, &*loan_path, loan);
// by this check: false
//
// let mut v = ~[1, 2, 3];
// let p = &v;
// v = ~[4];
//
// In this case, creating `p` triggers a RESTR_MUTATE
// restriction on the path `v`.
//
// Here is a second, more subtle example:
//
// let mut v = ~[1, 2, 3];
// let p = &const v[0];
// v[0] = 4; // OK
// v[1] = 5; // OK
// v = ~[4, 5, 3]; // Error
//
// In this case, `p` is pointing to `v[0]`, and it is a
// `const` pointer in any case. So the first two
// assignments are legal (and would be permitted by this
// check). However, the final assignment (which is
// logically equivalent) is forbidden, because it would
// cause the existing `v` array to be freed, thus
// invalidating `p`. In the code, this error results
// because `gather_loans::restrictions` adds a
// `RESTR_MUTATE` restriction whenever the contents of an
// owned pointer are borrowed, and hence while `v[*]` is not
// restricted from being written, `v` is.
let cont = this.each_in_scope_restriction(assignment_id,
&*loan_path,
|loan, restr| {
if restr.set.intersects(RESTR_MUTATE) {
this.report_illegal_mutation(assignment_span, &*loan_path, loan);
false
} else {
true
}
}); });
if !cont { return false }
// The previous code handled assignments to paths that
// have been restricted. This covers paths that have been
// directly lent out and their base paths, but does not
// cover random extensions of those paths. For example,
// the following program is not declared illegal by the
// previous check:
//
// let mut v = ~[1, 2, 3];
// let p = &v;
// v[0] = 4; // declared error by loop below, not code above
//
// The reason that this passes the previous check whereas
// an assignment like `v = ~[4]` fails is because the assignment
// here is to `v[*]`, and the existing restrictions were issued
// for `v`, not `v[*]`.
//
// So in this loop, we walk back up the loan path so long
// as the mutability of the path is dependent on a super
// path, and check that the super path was not lent out as
// mutable or immutable (a const loan is ok).
//
// Mutability of a path can be dependent on the super path
// in two ways. First, it might be inherited mutability.
// Second, the pointee of an `&mut` pointer can only be
// mutated if it is found in an unaliased location, so we
// have to check that the owner location is not borrowed.
//
// Note that we are *not* checking for any and all
// restrictions. We are only interested in the pointers
// that the user created, whereas we add restrictions for
// all kinds of paths that are not directly aliased. If we checked
// for all restrictions, and not just loans, then the following
// valid program would be considered illegal:
//
// let mut v = ~[1, 2, 3];
// let p = &const v[0];
// v[1] = 5; // ok
//
// Here the restriction that `v` not be mutated would be misapplied
// to block the subpath `v[1]`.
let full_loan_path = loan_path.clone();
let mut loan_path = loan_path;
loop {
loan_path = match *loan_path {
// Peel back one layer if, for `loan_path` to be
// mutable, `lp_base` must be mutable. This occurs
// with inherited mutability, owned pointers and
// `&mut` pointers.
LpExtend(ref lp_base, mc::McInherited, _) |
LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) |
LpExtend(ref lp_base, _, LpDeref(mc::GcPtr)) |
LpExtend(ref lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => {
lp_base.clone()
}
// Otherwise stop iterating
LpExtend(_, mc::McDeclared, _) |
LpExtend(_, mc::McImmutable, _) |
LpVar(_) => {
return true;
}
};
// Check for a non-const loan of `loan_path`
let cont = this.each_in_scope_loan(assignment_id, |loan| {
if loan.loan_path == loan_path {
this.report_illegal_mutation(assignment_span, &*full_loan_path, loan);
false
} else {
true
}
});
if !cont { return false }
}
} }
} }

View file

@ -259,7 +259,7 @@ impl<'a> GatherLoanCtxt<'a> {
// loan is safe. // loan is safe.
let restr = restrictions::compute_restrictions( let restr = restrictions::compute_restrictions(
self.bccx, borrow_span, cause, self.bccx, borrow_span, cause,
cmt.clone(), loan_region, self.restriction_set(req_kind)); cmt.clone(), loan_region);
// Create the loan record (if needed). // Create the loan record (if needed).
let loan = match restr { let loan = match restr {
@ -268,7 +268,7 @@ impl<'a> GatherLoanCtxt<'a> {
return; return;
} }
restrictions::SafeIf(loan_path, restrictions) => { restrictions::SafeIf(loan_path, restricted_paths) => {
let loan_scope = match loan_region { let loan_scope = match loan_region {
ty::ReScope(id) => id, ty::ReScope(id) => id,
ty::ReFree(ref fr) => fr.scope_id, ty::ReFree(ref fr) => fr.scope_id,
@ -314,7 +314,7 @@ impl<'a> GatherLoanCtxt<'a> {
gen_scope: gen_scope, gen_scope: gen_scope,
kill_scope: kill_scope, kill_scope: kill_scope,
span: borrow_span, span: borrow_span,
restrictions: restrictions, restricted_paths: restricted_paths,
cause: cause, cause: cause,
} }
} }
@ -390,21 +390,6 @@ impl<'a> GatherLoanCtxt<'a> {
} }
} }
fn restriction_set(&self, req_kind: ty::BorrowKind) -> RestrictionSet {
match req_kind {
// If borrowing data as immutable, no mutation allowed:
ty::ImmBorrow => RESTR_MUTATE,
// If borrowing data as mutable, no mutation nor other
// borrows allowed:
ty::MutBorrow => RESTR_MUTATE | RESTR_FREEZE,
// If borrowing data as unique imm, no mutation nor other
// borrows allowed:
ty::UniqueImmBorrow => RESTR_MUTATE | RESTR_FREEZE,
}
}
pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) { pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
//! For mutable loans of content whose mutability derives //! For mutable loans of content whose mutability derives
//! from a local variable, mark the mutability decl as necessary. //! from a local variable, mark the mutability decl as necessary.

View file

@ -23,15 +23,14 @@ use std::rc::Rc;
pub enum RestrictionResult { pub enum RestrictionResult {
Safe, Safe,
SafeIf(Rc<LoanPath>, Vec<Restriction>) SafeIf(Rc<LoanPath>, Vec<Rc<LoanPath>>)
} }
pub fn compute_restrictions(bccx: &BorrowckCtxt, pub fn compute_restrictions(bccx: &BorrowckCtxt,
span: Span, span: Span,
cause: euv::LoanCause, cause: euv::LoanCause,
cmt: mc::cmt, cmt: mc::cmt,
loan_region: ty::Region, loan_region: ty::Region) -> RestrictionResult {
restr: RestrictionSet) -> RestrictionResult {
let ctxt = RestrictionsContext { let ctxt = RestrictionsContext {
bccx: bccx, bccx: bccx,
span: span, span: span,
@ -39,7 +38,7 @@ pub fn compute_restrictions(bccx: &BorrowckCtxt,
loan_region: loan_region, loan_region: loan_region,
}; };
ctxt.restrict(cmt, restr) ctxt.restrict(cmt)
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -54,11 +53,8 @@ struct RestrictionsContext<'a> {
impl<'a> RestrictionsContext<'a> { impl<'a> RestrictionsContext<'a> {
fn restrict(&self, fn restrict(&self,
cmt: mc::cmt, cmt: mc::cmt) -> RestrictionResult {
restrictions: RestrictionSet) -> RestrictionResult { debug!("restrict(cmt={})", cmt.repr(self.bccx.tcx));
debug!("restrict(cmt={}, restrictions={})",
cmt.repr(self.bccx.tcx),
restrictions.repr(self.bccx.tcx));
match cmt.cat.clone() { match cmt.cat.clone() {
mc::cat_rvalue(..) => { mc::cat_rvalue(..) => {
@ -75,19 +71,14 @@ impl<'a> RestrictionsContext<'a> {
mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => { mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => {
// R-Variable // R-Variable
let lp = Rc::new(LpVar(local_id)); let lp = Rc::new(LpVar(local_id));
SafeIf(lp.clone(), vec!(Restriction { SafeIf(lp.clone(), vec!(lp))
loan_path: lp,
set: restrictions
}))
} }
mc::cat_downcast(cmt_base) => { mc::cat_downcast(cmt_base) => {
// When we borrow the interior of an enum, we have to // When we borrow the interior of an enum, we have to
// ensure the enum itself is not mutated, because that // ensure the enum itself is not mutated, because that
// could cause the type of the memory to change. // could cause the type of the memory to change.
self.restrict( self.restrict(cmt_base)
cmt_base,
restrictions | RESTR_MUTATE)
} }
mc::cat_interior(cmt_base, i) => { mc::cat_interior(cmt_base, i) => {
@ -96,8 +87,8 @@ impl<'a> RestrictionsContext<'a> {
// Overwriting the base would not change the type of // Overwriting the base would not change the type of
// the memory, so no additional restrictions are // the memory, so no additional restrictions are
// needed. // needed.
let result = self.restrict(cmt_base, restrictions); let result = self.restrict(cmt_base);
self.extend(result, cmt.mutbl, LpInterior(i), restrictions) self.extend(result, cmt.mutbl, LpInterior(i))
} }
mc::cat_deref(cmt_base, _, pk @ mc::OwnedPtr) | mc::cat_deref(cmt_base, _, pk @ mc::OwnedPtr) |
@ -112,10 +103,8 @@ impl<'a> RestrictionsContext<'a> {
// same, because this could be the last ref. // same, because this could be the last ref.
// Eventually we should make these non-special and // Eventually we should make these non-special and
// just rely on Deref<T> implementation. // just rely on Deref<T> implementation.
let result = self.restrict( let result = self.restrict(cmt_base);
cmt_base, self.extend(result, cmt.mutbl, LpDeref(pk))
restrictions | RESTR_MUTATE);
self.extend(result, cmt.mutbl, LpDeref(pk), restrictions)
} }
mc::cat_copied_upvar(..) | // FIXME(#2152) allow mutation of upvars mc::cat_copied_upvar(..) | // FIXME(#2152) allow mutation of upvars
@ -133,7 +122,7 @@ impl<'a> RestrictionsContext<'a> {
cause: self.cause, cause: self.cause,
cmt: cmt_base, cmt: cmt_base,
code: err_borrowed_pointer_too_short( code: err_borrowed_pointer_too_short(
self.loan_region, lt, restrictions)}); self.loan_region, lt)});
return Safe; return Safe;
} }
Safe Safe
@ -148,12 +137,12 @@ impl<'a> RestrictionsContext<'a> {
cause: self.cause, cause: self.cause,
cmt: cmt_base, cmt: cmt_base,
code: err_borrowed_pointer_too_short( code: err_borrowed_pointer_too_short(
self.loan_region, lt, restrictions)}); self.loan_region, lt)});
return Safe; return Safe;
} }
let result = self.restrict(cmt_base, restrictions); let result = self.restrict(cmt_base);
self.extend(result, cmt.mutbl, LpDeref(pk), restrictions) self.extend(result, cmt.mutbl, LpDeref(pk))
} }
mc::cat_deref(_, _, mc::UnsafePtr(..)) => { mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
@ -162,7 +151,7 @@ impl<'a> RestrictionsContext<'a> {
} }
mc::cat_discr(cmt_base, _) => { mc::cat_discr(cmt_base, _) => {
self.restrict(cmt_base, restrictions) self.restrict(cmt_base)
} }
} }
} }
@ -170,16 +159,12 @@ impl<'a> RestrictionsContext<'a> {
fn extend(&self, fn extend(&self,
result: RestrictionResult, result: RestrictionResult,
mc: mc::MutabilityCategory, mc: mc::MutabilityCategory,
elem: LoanPathElem, elem: LoanPathElem) -> RestrictionResult {
restrictions: RestrictionSet) -> RestrictionResult {
match result { match result {
Safe => Safe, Safe => Safe,
SafeIf(base_lp, mut base_vec) => { SafeIf(base_lp, mut base_vec) => {
let lp = Rc::new(LpExtend(base_lp, mc, elem)); let lp = Rc::new(LpExtend(base_lp, mc, elem));
base_vec.push(Restriction { base_vec.push(lp.clone());
loan_path: lp.clone(),
set: restrictions
});
SafeIf(lp, base_vec) SafeIf(lp, base_vec)
} }
} }

View file

@ -21,7 +21,6 @@ use middle::ty;
use util::ppaux::{note_and_explain_region, Repr, UserString}; use util::ppaux::{note_and_explain_region, Repr, UserString};
use std::cell::{Cell}; use std::cell::{Cell};
use std::ops::{BitOr, BitAnd};
use std::rc::Rc; use std::rc::Rc;
use std::gc::{Gc, GC}; use std::gc::{Gc, GC};
use std::string::String; use std::string::String;
@ -182,7 +181,7 @@ pub struct Loan {
index: uint, index: uint,
loan_path: Rc<LoanPath>, loan_path: Rc<LoanPath>,
kind: ty::BorrowKind, kind: ty::BorrowKind,
restrictions: Vec<Restriction>, restricted_paths: Vec<Rc<LoanPath>>,
gen_scope: ast::NodeId, gen_scope: ast::NodeId,
kill_scope: ast::NodeId, kill_scope: ast::NodeId,
span: Span, span: Span,
@ -250,58 +249,6 @@ pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
} }
} }
///////////////////////////////////////////////////////////////////////////
// Restrictions
//
// Borrowing an lvalue often results in *restrictions* that limit what
// can be done with this lvalue during the scope of the loan:
//
// - `RESTR_MUTATE`: The lvalue may not be modified or `&mut` borrowed.
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
//
// In addition, no value which is restricted may be moved. Therefore,
// restrictions are meaningful even if the RestrictionSet is empty,
// because the restriction against moves is implied.
pub struct Restriction {
loan_path: Rc<LoanPath>,
set: RestrictionSet
}
#[deriving(PartialEq)]
pub struct RestrictionSet {
bits: u32
}
#[allow(dead_code)] // potentially useful
pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000};
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0010};
impl RestrictionSet {
pub fn intersects(&self, restr: RestrictionSet) -> bool {
(self.bits & restr.bits) != 0
}
}
impl BitOr<RestrictionSet,RestrictionSet> for RestrictionSet {
fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet {
RestrictionSet {bits: self.bits | rhs.bits}
}
}
impl BitAnd<RestrictionSet,RestrictionSet> for RestrictionSet {
fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet {
RestrictionSet {bits: self.bits & rhs.bits}
}
}
impl Repr for RestrictionSet {
fn repr(&self, _tcx: &ty::ctxt) -> String {
format!("RestrictionSet(0x{:x})", self.bits as uint)
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Errors // Errors
@ -310,8 +257,7 @@ impl Repr for RestrictionSet {
pub enum bckerr_code { pub enum bckerr_code {
err_mutbl, err_mutbl,
err_out_of_scope(ty::Region, ty::Region), // superscope, subscope err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
err_borrowed_pointer_too_short( err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
ty::Region, ty::Region, RestrictionSet), // loan, ptr
} }
// Combination of an error code and the categorization of the expression // Combination of an error code and the categorization of the expression
@ -711,7 +657,7 @@ impl<'a> BorrowckCtxt<'a> {
suggestion); suggestion);
} }
err_borrowed_pointer_too_short(loan_scope, ptr_scope, _) => { err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
let descr = match opt_loan_path(&err.cmt) { let descr = match opt_loan_path(&err.cmt) {
Some(lp) => { Some(lp) => {
format!("`{}`", self.loan_path_to_str(&*lp)) format!("`{}`", self.loan_path_to_str(&*lp))
@ -827,15 +773,7 @@ impl Repr for Loan {
self.kind, self.kind,
self.gen_scope, self.gen_scope,
self.kill_scope, self.kill_scope,
self.restrictions.repr(tcx))).to_string() self.restricted_paths.repr(tcx))).to_string()
}
}
impl Repr for Restriction {
fn repr(&self, tcx: &ty::ctxt) -> String {
(format!("Restriction({}, {:x})",
self.loan_path.repr(tcx),
self.set.bits as uint)).to_string()
} }
} }