Skip to content

Commit 8495ed4

Browse files
committed
loan: Track whether or not the current path owns the data being
lent. We can be more liberal with respect to the scope of the loan if we do not own the data being lent, which used to be impossible but can now occur with `&mut`.
1 parent b919d47 commit 8495ed4

File tree

1 file changed

+128
-56
lines changed
  • src/librustc/middle/borrowck

1 file changed

+128
-56
lines changed

src/librustc/middle/borrowck/loan.rs

+128-56
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,35 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
/*!
12+
13+
The `Loan` module deals with borrows of *uniquely mutable* data. We
14+
say that data is uniquely mutable if the current activation (stack
15+
frame) controls the only mutable reference to the data. The most
16+
common way that this can occur is if the current activation owns the
17+
data being borrowed, but it can also occur with `&mut` pointers. The
18+
primary characteristic of uniquely mutable data is that, at any given
19+
time, there is at most one path that can be used to mutate it, and
20+
that path is only accessible from the top stack frame.
21+
22+
Given that some data found at a path P is being borrowed to a borrowed
23+
pointer with mutability M and lifetime L, the job of the code in this
24+
module is to compute the set of *loans* that are necessary to ensure
25+
that (1) the data found at P outlives L and that (2) if M is mutable
26+
then the path P will not be modified directly or indirectly except
27+
through that pointer. A *loan* is the combination of a path P_L, a
28+
mutability M_L, and a lifetime L_L where:
29+
30+
- The path P_L indicates what data has been lent.
31+
- The mutability M_L indicates the access rights on the data:
32+
- const: the data cannot be moved
33+
- immutable/mutable: the data cannot be moved or mutated
34+
- The lifetime L_L indicates the *scope* of the loan.
35+
36+
XXX --- much more needed, don't have time to write this all up now
37+
38+
*/
39+
1140
// ----------------------------------------------------------------------
1241
// Loan(Ex, M, S) = Ls holds if ToAddr(Ex) will remain valid for the entirety
1342
// of the scope S, presuming that the returned set of loans `Ls` are honored.
@@ -39,7 +68,7 @@ impl borrowck_ctxt {
3968
scope_region: scope_region,
4069
loans: ~[]
4170
};
42-
match lc.loan(cmt, mutbl) {
71+
match lc.loan(cmt, mutbl, true) {
4372
Err(ref e) => Err((*e)),
4473
Ok(()) => {
4574
let LoanContext {loans, _} = move lc;
@@ -62,46 +91,25 @@ struct LoanContext {
6291
impl LoanContext {
6392
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
6493

65-
fn issue_loan(&self,
66-
cmt: cmt,
67-
scope_ub: ty::Region,
68-
req_mutbl: ast::mutability) -> bckres<()> {
69-
if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
70-
match req_mutbl {
71-
m_mutbl => {
72-
// We do not allow non-mutable data to be loaned
73-
// out as mutable under any circumstances.
74-
if cmt.mutbl != m_mutbl {
75-
return Err({cmt:cmt,
76-
code:err_mutbl(req_mutbl)});
77-
}
78-
}
79-
m_const | m_imm => {
80-
// However, mutable data can be loaned out as
81-
// immutable (and any data as const). The
82-
// `check_loans` pass will then guarantee that no
83-
// writes occur for the duration of the loan.
84-
}
85-
}
94+
fn loan(&self,
95+
cmt: cmt,
96+
req_mutbl: ast::mutability,
97+
owns_lent_data: bool) -> bckres<()>
98+
{
99+
/*!
100+
*
101+
* The main routine.
102+
*
103+
* # Parameters
104+
*
105+
* - `cmt`: the categorization of the data being borrowed
106+
* - `req_mutbl`: the mutability of the borrowed pointer
107+
* that was created
108+
* - `owns_lent_data`: indicates whether `cmt` owns the
109+
* data that is being lent. See
110+
* discussion in `issue_loan()`.
111+
*/
86112

87-
self.loans.push(Loan {
88-
// Note: cmt.lp must be Some(_) because otherwise this
89-
// loan process does not apply at all.
90-
lp: cmt.lp.get(),
91-
cmt: cmt,
92-
mutbl: req_mutbl
93-
});
94-
return Ok(());
95-
} else {
96-
// The loan being requested lives longer than the data
97-
// being loaned out!
98-
return Err({cmt:cmt,
99-
code:err_out_of_scope(scope_ub,
100-
self.scope_region)});
101-
}
102-
}
103-
104-
fn loan(&self, cmt: cmt, req_mutbl: ast::mutability) -> bckres<()> {
105113
debug!("loan(%s, %s)",
106114
self.bccx.cmt_to_repr(cmt),
107115
self.bccx.mut_to_str(req_mutbl));
@@ -123,13 +131,14 @@ impl LoanContext {
123131
}
124132
cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
125133
let local_scope_id = self.tcx().region_map.get(local_id);
126-
self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl)
134+
self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl,
135+
owns_lent_data)
127136
}
128137
cat_stack_upvar(cmt) => {
129-
self.loan(cmt, req_mutbl) // NDM correct?
138+
self.loan(cmt, req_mutbl, owns_lent_data)
130139
}
131140
cat_discr(base, _) => {
132-
self.loan(base, req_mutbl)
141+
self.loan(base, req_mutbl, owns_lent_data)
133142
}
134143
cat_comp(cmt_base, comp_field(_, m)) |
135144
cat_comp(cmt_base, comp_index(_, m)) => {
@@ -139,36 +148,41 @@ impl LoanContext {
139148
// that case, it must also be embedded in an immutable
140149
// location, or else the whole structure could be
141150
// overwritten and the component along with it.
142-
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m)
151+
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m,
152+
owns_lent_data)
143153
}
144154
cat_comp(cmt_base, comp_tuple) |
145155
cat_comp(cmt_base, comp_anon_field) => {
146156
// As above.
147-
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm)
157+
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm,
158+
owns_lent_data)
148159
}
149160
cat_comp(cmt_base, comp_variant(enum_did)) => {
150161
// For enums, the memory is unstable if there are multiple
151162
// variants, because if the enum value is overwritten then
152163
// the memory changes type.
153164
if ty::enum_is_univariant(self.bccx.tcx, enum_did) {
154-
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm)
165+
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm,
166+
owns_lent_data)
155167
} else {
156-
self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
168+
self.loan_unstable_deref(cmt, cmt_base, req_mutbl,
169+
owns_lent_data)
157170
}
158171
}
159172
cat_deref(cmt_base, _, uniq_ptr) => {
160173
// For unique pointers, the memory being pointed out is
161174
// unstable because if the unique pointer is overwritten
162175
// then the memory is freed.
163-
self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
176+
self.loan_unstable_deref(cmt, cmt_base, req_mutbl,
177+
owns_lent_data)
164178
}
165179
cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => {
166180
// Mutable data can be loaned out as immutable or const. We must
167181
// loan out the base as well as the main memory. For example,
168182
// if someone borrows `*b`, we want to borrow `b` as immutable
169183
// as well.
170-
do self.loan(cmt_base, m_imm).chain |_| {
171-
self.issue_loan(cmt, region, m_const)
184+
do self.loan(cmt_base, m_imm, false).chain |_| {
185+
self.issue_loan(cmt, region, m_const, owns_lent_data)
172186
}
173187
}
174188
cat_deref(_, _, unsafe_ptr) |
@@ -189,7 +203,8 @@ impl LoanContext {
189203
cmt: cmt,
190204
cmt_base: cmt,
191205
req_mutbl: ast::mutability,
192-
comp_mutbl: ast::mutability) -> bckres<()> {
206+
comp_mutbl: ast::mutability,
207+
owns_lent_data: bool) -> bckres<()> {
193208
// Determine the mutability that the base component must have,
194209
// given the required mutability of the pointer (`req_mutbl`)
195210
// and the declared mutability of the component (`comp_mutbl`).
@@ -243,10 +258,11 @@ impl LoanContext {
243258
(m_const, _) => m_const // (5)
244259
};
245260

246-
do self.loan(cmt_base, base_mutbl).chain |_ok| {
261+
do self.loan(cmt_base, base_mutbl, owns_lent_data).chain |_ok| {
247262
// can use static for the scope because the base
248263
// determines the lifetime, ultimately
249-
self.issue_loan(cmt, ty::re_static, req_mutbl)
264+
self.issue_loan(cmt, ty::re_static, req_mutbl,
265+
owns_lent_data)
250266
}
251267
}
252268

@@ -256,13 +272,69 @@ impl LoanContext {
256272
fn loan_unstable_deref(&self,
257273
cmt: cmt,
258274
cmt_base: cmt,
259-
req_mutbl: ast::mutability) -> bckres<()> {
275+
req_mutbl: ast::mutability,
276+
owns_lent_data: bool) -> bckres<()>
277+
{
260278
// Variant components: the base must be immutable, because
261279
// if it is overwritten, the types of the embedded data
262280
// could change.
263-
do self.loan(cmt_base, m_imm).chain |_| {
281+
do self.loan(cmt_base, m_imm, owns_lent_data).chain |_| {
264282
// can use static, as in loan_stable_comp()
265-
self.issue_loan(cmt, ty::re_static, req_mutbl)
283+
self.issue_loan(cmt, ty::re_static, req_mutbl,
284+
owns_lent_data)
285+
}
286+
}
287+
288+
fn issue_loan(&self,
289+
cmt: cmt,
290+
scope_ub: ty::Region,
291+
req_mutbl: ast::mutability,
292+
owns_lent_data: bool) -> bckres<()>
293+
{
294+
// Subtle: the `scope_ub` is the maximal lifetime of `cmt`.
295+
// Therefore, if `cmt` owns the data being lent, then the
296+
// scope of the loan must be less than `scope_ub`, or else the
297+
// data would be freed while the loan is active.
298+
//
299+
// However, if `cmt` does *not* own the data being lent, then
300+
// it is ok if `cmt` goes out of scope during the loan. This
301+
// can occur when you have an `&mut` parameter that is being
302+
// reborrowed.
303+
304+
if !owns_lent_data ||
305+
self.bccx.is_subregion_of(self.scope_region, scope_ub)
306+
{
307+
match req_mutbl {
308+
m_mutbl => {
309+
// We do not allow non-mutable data to be loaned
310+
// out as mutable under any circumstances.
311+
if cmt.mutbl != m_mutbl {
312+
return Err({cmt:cmt,
313+
code:err_mutbl(req_mutbl)});
314+
}
315+
}
316+
m_const | m_imm => {
317+
// However, mutable data can be loaned out as
318+
// immutable (and any data as const). The
319+
// `check_loans` pass will then guarantee that no
320+
// writes occur for the duration of the loan.
321+
}
322+
}
323+
324+
self.loans.push(Loan {
325+
// Note: cmt.lp must be Some(_) because otherwise this
326+
// loan process does not apply at all.
327+
lp: cmt.lp.get(),
328+
cmt: cmt,
329+
mutbl: req_mutbl
330+
});
331+
return Ok(());
332+
} else {
333+
// The loan being requested lives longer than the data
334+
// being loaned out!
335+
return Err({cmt:cmt,
336+
code:err_out_of_scope(scope_ub,
337+
self.scope_region)});
266338
}
267339
}
268340
}

0 commit comments

Comments
 (0)