Skip to content

Commit 54553f4

Browse files
committed
Check whether loans conflict with old loans or with themselves.
Along the way, convert from dvec-of-dvec representation to track loans in scope to just a single flattened list. It's more convenient. No review: small bug fix. Fixes rust-lang#3765.
1 parent 07edf90 commit 54553f4

File tree

5 files changed

+165
-94
lines changed

5 files changed

+165
-94
lines changed

src/rustc/middle/borrowck.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ impl bckerr : cmp::Eq {
383383
type bckres<T> = Result<T, bckerr>;
384384

385385
/// a complete record of a loan that was granted
386-
type loan = {lp: @loan_path, cmt: cmt, mutbl: ast::mutability};
386+
struct Loan {lp: @loan_path, cmt: cmt, mutbl: ast::mutability}
387387

388388
/// maps computed by `gather_loans` that are then used by `check_loans`
389389
///
@@ -392,7 +392,7 @@ type loan = {lp: @loan_path, cmt: cmt, mutbl: ast::mutability};
392392
/// - `pure_map`: map from block/expr that must be pure to the error message
393393
/// that should be reported if they are not pure
394394
type req_maps = {
395-
req_loan_map: HashMap<ast::node_id, @DVec<@DVec<loan>>>,
395+
req_loan_map: HashMap<ast::node_id, @DVec<Loan>>,
396396
pure_map: HashMap<ast::node_id, bckerr>
397397
};
398398

@@ -582,6 +582,11 @@ impl borrowck_ctxt {
582582
method_map: self.method_map};
583583
mc.mut_to_str(mutbl)
584584
}
585+
586+
fn loan_to_repr(loan: &Loan) -> ~str {
587+
fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
588+
loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
589+
}
585590
}
586591

587592
// The inherent mutability of a component is its default mutability

src/rustc/middle/borrowck/check_loans.rs

+52-33
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,15 @@ impl check_loan_ctxt {
131131
}
132132
}
133133
134-
fn walk_loans(scope_id: ast::node_id,
135-
f: fn(v: &loan) -> bool) {
134+
fn walk_loans(scope_id: ast::node_id, f: fn(v: &Loan) -> bool) {
136135
let mut scope_id = scope_id;
137136
let region_map = self.tcx().region_map;
138137
let req_loan_map = self.req_maps.req_loan_map;
139138
140139
loop {
141-
for req_loan_map.find(scope_id).each |loanss| {
142-
for loanss.each |loans| {
143-
for loans.each |loan| {
144-
if !f(loan) { return; }
145-
}
140+
for req_loan_map.find(scope_id).each |loans| {
141+
for loans.each |loan| {
142+
if !f(loan) { return; }
146143
}
147144
}
148145
@@ -155,7 +152,7 @@ impl check_loan_ctxt {
155152
156153
fn walk_loans_of(scope_id: ast::node_id,
157154
lp: @loan_path,
158-
f: fn(v: &loan) -> bool) {
155+
f: fn(v: &Loan) -> bool) {
159156
for self.walk_loans(scope_id) |loan| {
160157
if loan.lp == lp {
161158
if !f(loan) { return; }
@@ -256,36 +253,58 @@ impl check_loan_ctxt {
256253
}
257254

258255
fn check_for_conflicting_loans(scope_id: ast::node_id) {
259-
let new_loanss = match self.req_maps.req_loan_map.find(scope_id) {
256+
debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
257+
258+
let new_loans = match self.req_maps.req_loan_map.find(scope_id) {
260259
None => return,
261-
Some(loanss) => loanss
260+
Some(loans) => loans
262261
};
263262

263+
debug!("new_loans has length %?", new_loans.len());
264+
264265
let par_scope_id = self.tcx().region_map.get(scope_id);
265266
for self.walk_loans(par_scope_id) |old_loan| {
266-
for new_loanss.each |new_loans| {
267-
for new_loans.each |new_loan| {
268-
if old_loan.lp != new_loan.lp { loop; }
269-
match (old_loan.mutbl, new_loan.mutbl) {
270-
(m_const, _) | (_, m_const) |
271-
(m_mutbl, m_mutbl) | (m_imm, m_imm) => {
272-
/*ok*/
273-
}
274-
275-
(m_mutbl, m_imm) | (m_imm, m_mutbl) => {
276-
self.bccx.span_err(
277-
new_loan.cmt.span,
278-
fmt!("loan of %s as %s \
279-
conflicts with prior loan",
280-
self.bccx.cmt_to_str(new_loan.cmt),
281-
self.bccx.mut_to_str(new_loan.mutbl)));
282-
self.bccx.span_note(
283-
old_loan.cmt.span,
284-
fmt!("prior loan as %s granted here",
285-
self.bccx.mut_to_str(old_loan.mutbl)));
286-
}
287-
}
288-
}
267+
debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan));
268+
269+
for new_loans.each |new_loan| {
270+
self.report_error_if_loans_conflict(old_loan, new_loan);
271+
}
272+
}
273+
274+
let len = new_loans.len();
275+
for uint::range(0, len) |i| {
276+
let loan_i = new_loans[i];
277+
for uint::range(i+1, len) |j| {
278+
let loan_j = new_loans[j];
279+
self.report_error_if_loans_conflict(&loan_i, &loan_j);
280+
}
281+
}
282+
}
283+
284+
fn report_error_if_loans_conflict(&self,
285+
old_loan: &Loan,
286+
new_loan: &Loan) {
287+
if old_loan.lp != new_loan.lp {
288+
return;
289+
}
290+
291+
match (old_loan.mutbl, new_loan.mutbl) {
292+
(m_const, _) | (_, m_const) |
293+
(m_mutbl, m_mutbl) | (m_imm, m_imm) => {
294+
/*ok*/
295+
}
296+
297+
(m_mutbl, m_imm) | (m_imm, m_mutbl) => {
298+
self.bccx.span_err(
299+
new_loan.cmt.span,
300+
fmt!("loan of %s as %s \
301+
conflicts with prior loan",
302+
self.bccx.cmt_to_str(new_loan.cmt),
303+
self.bccx.mut_to_str(new_loan.mutbl)));
304+
self.bccx.span_note(
305+
old_loan.cmt.span,
306+
fmt!("prior loan as %s granted here",
307+
self.bccx.mut_to_str(old_loan.mutbl)));
289308
}
290309
}
291310
}

src/rustc/middle/borrowck/gather_loans.rs

+56-39
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,10 @@ fn req_loans_in_expr(ex: @ast::expr,
213213
}
214214

215215
impl gather_loan_ctxt {
216-
fn tcx() -> ty::ctxt { self.bccx.tcx }
216+
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
217217

218-
fn guarantee_adjustments(expr: @ast::expr,
218+
fn guarantee_adjustments(&self,
219+
expr: @ast::expr,
219220
adjustment: &ty::AutoAdjustment) {
220221
debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
221222
expr_repr(self.tcx(), expr), adjustment);
@@ -256,7 +257,8 @@ impl gather_loan_ctxt {
256257
// out loans, which will be added to the `req_loan_map`. This can
257258
// also entail "rooting" GC'd pointers, which means ensuring
258259
// dynamically that they are not freed.
259-
fn guarantee_valid(cmt: cmt,
260+
fn guarantee_valid(&self,
261+
cmt: cmt,
260262
req_mutbl: ast::mutability,
261263
scope_r: ty::region) {
262264

@@ -280,35 +282,12 @@ impl gather_loan_ctxt {
280282
// it within that scope, the loan will be detected and an
281283
// error will be reported.
282284
Some(_) => {
283-
match self.bccx.loan(cmt, scope_r, req_mutbl) {
284-
Err(e) => { self.bccx.report(e); }
285-
Ok(loans) if loans.len() == 0 => {}
286-
Ok(loans) => {
287-
match scope_r {
288-
ty::re_scope(scope_id) => {
289-
self.add_loans(scope_id, loans);
290-
291-
if req_mutbl == m_imm && cmt.mutbl != m_imm {
292-
self.bccx.loaned_paths_imm += 1;
293-
294-
if self.tcx().sess.borrowck_note_loan() {
295-
self.bccx.span_note(
296-
cmt.span,
297-
fmt!("immutable loan required"));
298-
}
299-
} else {
300-
self.bccx.loaned_paths_same += 1;
301-
}
285+
match self.bccx.loan(cmt, scope_r, req_mutbl) {
286+
Err(e) => { self.bccx.report(e); }
287+
Ok(move loans) => {
288+
self.add_loans(cmt, req_mutbl, scope_r, move loans);
302289
}
303-
_ => {
304-
self.bccx.tcx.sess.span_bug(
305-
cmt.span,
306-
fmt!("loans required but scope is scope_region is %s",
307-
region_to_str(self.tcx(), scope_r)));
308-
}
309-
}
310290
}
311-
}
312291
}
313292

314293
// The path is not loanable: in that case, we must try and
@@ -385,7 +364,8 @@ impl gather_loan_ctxt {
385364
// has type `@mut{f:int}`, this check might fail because `&x.f`
386365
// reqires an immutable pointer, but `f` lives in (aliased)
387366
// mutable memory.
388-
fn check_mutbl(req_mutbl: ast::mutability,
367+
fn check_mutbl(&self,
368+
req_mutbl: ast::mutability,
389369
cmt: cmt) -> bckres<preserve_condition> {
390370
debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)",
391371
req_mutbl, cmt.mutbl);
@@ -407,21 +387,58 @@ impl gather_loan_ctxt {
407387
}
408388
}
409389

410-
fn add_loans(scope_id: ast::node_id, loans: @DVec<loan>) {
390+
fn add_loans(&self,
391+
cmt: cmt,
392+
req_mutbl: ast::mutability,
393+
scope_r: ty::region,
394+
+loans: ~[Loan]) {
395+
if loans.len() == 0 {
396+
return;
397+
}
398+
399+
let scope_id = match scope_r {
400+
ty::re_scope(scope_id) => scope_id,
401+
_ => {
402+
self.bccx.tcx.sess.span_bug(
403+
cmt.span,
404+
fmt!("loans required but scope is scope_region is %s",
405+
region_to_str(self.tcx(), scope_r)));
406+
}
407+
};
408+
409+
self.add_loans_to_scope_id(scope_id, move loans);
410+
411+
if req_mutbl == m_imm && cmt.mutbl != m_imm {
412+
self.bccx.loaned_paths_imm += 1;
413+
414+
if self.tcx().sess.borrowck_note_loan() {
415+
self.bccx.span_note(
416+
cmt.span,
417+
fmt!("immutable loan required"));
418+
}
419+
} else {
420+
self.bccx.loaned_paths_same += 1;
421+
}
422+
}
423+
424+
fn add_loans_to_scope_id(&self, scope_id: ast::node_id, +loans: ~[Loan]) {
411425
debug!("adding %u loans to scope_id %?", loans.len(), scope_id);
412426
match self.req_maps.req_loan_map.find(scope_id) {
413-
Some(l) => {
414-
l.push(loans);
427+
Some(req_loans) => {
428+
req_loans.push_all(loans);
415429
}
416430
None => {
417-
self.req_maps.req_loan_map.insert(
418-
scope_id, @dvec::from_vec(~[loans]));
431+
let dvec = @dvec::from_vec(move loans);
432+
self.req_maps.req_loan_map.insert(scope_id, dvec);
419433
}
420434
}
421435
}
422436

423-
fn gather_pat(discr_cmt: cmt, root_pat: @ast::pat,
424-
arm_id: ast::node_id, alt_id: ast::node_id) {
437+
fn gather_pat(&self,
438+
discr_cmt: cmt,
439+
root_pat: @ast::pat,
440+
arm_id: ast::node_id,
441+
alt_id: ast::node_id) {
425442
do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
426443
match pat.node {
427444
ast::pat_ident(bm, _, _) if !self.pat_is_variant(pat) => {
@@ -475,7 +492,7 @@ impl gather_loan_ctxt {
475492
}
476493
}
477494

478-
fn pat_is_variant(pat: @ast::pat) -> bool {
495+
fn pat_is_variant(&self, pat: @ast::pat) -> bool {
479496
pat_util::pat_is_variant(self.bccx.tcx.def_map, pat)
480497
}
481498
}

src/rustc/middle/borrowck/loan.rs

+25-20
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,37 @@ use result::{Result, Ok, Err};
88
impl borrowck_ctxt {
99
fn loan(cmt: cmt,
1010
scope_region: ty::region,
11-
mutbl: ast::mutability) -> bckres<@DVec<loan>> {
12-
let lc = loan_ctxt_(@{bccx: self,
13-
scope_region: scope_region,
14-
loans: @DVec()});
11+
mutbl: ast::mutability) -> bckres<~[Loan]> {
12+
let lc = LoanContext {
13+
bccx: self,
14+
scope_region: scope_region,
15+
loans: ~[]
16+
};
1517
match lc.loan(cmt, mutbl) {
16-
Ok(()) => {Ok(lc.loans)}
17-
Err(e) => {Err(e)}
18+
Err(e) => Err(e),
19+
Ok(()) => {
20+
let LoanContext {loans, _} = move lc;
21+
Ok(loans)
22+
}
1823
}
1924
}
2025
}
2126

22-
type loan_ctxt_ = {
27+
struct LoanContext {
2328
bccx: borrowck_ctxt,
2429

2530
// the region scope for which we must preserve the memory
2631
scope_region: ty::region,
2732

2833
// accumulated list of loans that will be required
29-
loans: @DVec<loan>
30-
};
31-
32-
enum loan_ctxt {
33-
loan_ctxt_(@loan_ctxt_)
34+
mut loans: ~[Loan]
3435
}
3536

36-
impl loan_ctxt {
37-
fn tcx() -> ty::ctxt { self.bccx.tcx }
37+
impl LoanContext {
38+
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
3839

39-
fn issue_loan(cmt: cmt,
40+
fn issue_loan(&self,
41+
cmt: cmt,
4042
scope_ub: ty::region,
4143
req_mutbl: ast::mutability) -> bckres<()> {
4244
if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
@@ -57,12 +59,13 @@ impl loan_ctxt {
5759
}
5860
}
5961

60-
(*self.loans).push({
62+
self.loans.push(Loan {
6163
// Note: cmt.lp must be Some(_) because otherwise this
6264
// loan process does not apply at all.
6365
lp: cmt.lp.get(),
6466
cmt: cmt,
65-
mutbl: req_mutbl});
67+
mutbl: req_mutbl
68+
});
6669
return Ok(());
6770
} else {
6871
// The loan being requested lives longer than the data
@@ -73,7 +76,7 @@ impl loan_ctxt {
7376
}
7477
}
7578

76-
fn loan(cmt: cmt, req_mutbl: ast::mutability) -> bckres<()> {
79+
fn loan(&self, cmt: cmt, req_mutbl: ast::mutability) -> bckres<()> {
7780
debug!("loan(%s, %s)",
7881
self.bccx.cmt_to_repr(cmt),
7982
self.bccx.mut_to_str(req_mutbl));
@@ -144,7 +147,8 @@ impl loan_ctxt {
144147
// A "stable component" is one where assigning the base of the
145148
// component cannot cause the component itself to change types.
146149
// Example: record fields.
147-
fn loan_stable_comp(cmt: cmt,
150+
fn loan_stable_comp(&self,
151+
cmt: cmt,
148152
cmt_base: cmt,
149153
req_mutbl: ast::mutability) -> bckres<()> {
150154
let base_mutbl = match req_mutbl {
@@ -162,7 +166,8 @@ impl loan_ctxt {
162166
// An "unstable deref" means a deref of a ptr/comp where, if the
163167
// base of the deref is assigned to, pointers into the result of the
164168
// deref would be invalidated. Examples: interior of variants, uniques.
165-
fn loan_unstable_deref(cmt: cmt,
169+
fn loan_unstable_deref(&self,
170+
cmt: cmt,
166171
cmt_base: cmt,
167172
req_mutbl: ast::mutability) -> bckres<()> {
168173
// Variant components: the base must be immutable, because

0 commit comments

Comments
 (0)