Skip to content

Commit 42c05fe

Browse files
committed
Correct propagation of mutability from components to base in borrowck
Fixes #3828.
1 parent 082d3d5 commit 42c05fe

File tree

4 files changed

+117
-9
lines changed

4 files changed

+117
-9
lines changed

src/rustc/middle/borrowck/loan.rs

+61-9
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,26 @@ impl LoanContext {
106106
cat_discr(base, _) => {
107107
self.loan(base, req_mutbl)
108108
}
109-
cat_comp(cmt_base, comp_field(*)) |
110-
cat_comp(cmt_base, comp_index(*)) |
111-
cat_comp(cmt_base, comp_tuple) => {
109+
cat_comp(cmt_base, comp_field(_, m)) |
110+
cat_comp(cmt_base, comp_index(_, m)) => {
112111
// For most components, the type of the embedded data is
113112
// stable. Therefore, the base structure need only be
114113
// const---unless the component must be immutable. In
115114
// that case, it must also be embedded in an immutable
116115
// location, or else the whole structure could be
117116
// overwritten and the component along with it.
118-
self.loan_stable_comp(cmt, cmt_base, req_mutbl)
117+
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m)
118+
}
119+
cat_comp(cmt_base, comp_tuple) => {
120+
// As above.
121+
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm)
119122
}
120123
cat_comp(cmt_base, comp_variant(enum_did)) => {
121124
// For enums, the memory is unstable if there are multiple
122125
// variants, because if the enum value is overwritten then
123126
// the memory changes type.
124127
if ty::enum_is_univariant(self.bccx.tcx, enum_did) {
125-
self.loan_stable_comp(cmt, cmt_base, req_mutbl)
128+
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm)
126129
} else {
127130
self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
128131
}
@@ -150,10 +153,59 @@ impl LoanContext {
150153
fn loan_stable_comp(&self,
151154
cmt: cmt,
152155
cmt_base: cmt,
153-
req_mutbl: ast::mutability) -> bckres<()> {
154-
let base_mutbl = match req_mutbl {
155-
m_imm => m_imm,
156-
m_const | m_mutbl => m_const
156+
req_mutbl: ast::mutability,
157+
comp_mutbl: ast::mutability) -> bckres<()> {
158+
// Determine the mutability that the base component must have,
159+
// given the required mutability of the pointer (`req_mutbl`)
160+
// and the declared mutability of the component (`comp_mutbl`).
161+
// This is surprisingly subtle.
162+
//
163+
// Note that the *declared* mutability of the component is not
164+
// necessarily the same as cmt.mutbl, since a component
165+
// declared as immutable but embedded in a mutable context
166+
// becomes mutable. It's best to think of comp_mutbl as being
167+
// either MUTABLE or DEFAULT, not MUTABLE or IMMUTABLE. We
168+
// should really patch up the AST to reflect this distinction.
169+
//
170+
// Let's consider the cases below:
171+
//
172+
// 1. mut required, mut declared: In this case, the base
173+
// component must merely be const. The reason is that it
174+
// does not matter if the base component is borrowed as
175+
// mutable or immutable, as the mutability of the base
176+
// component is overridden in the field declaration itself
177+
// (see `compile-fail/borrowck-mut-field-imm-base.rs`)
178+
//
179+
// 2. mut required, imm declared: This would only be legal if
180+
// the component is embeded in a mutable context. However,
181+
// we detect mismatches between the mutability of the value
182+
// as a whole and the required mutability in `issue_loan()`
183+
// above. In any case, presuming that the component IS
184+
// embedded in a mutable context, both the component and
185+
// the base must be loaned as MUTABLE. This is to ensure
186+
// that there is no loan of the base as IMMUTABLE, which
187+
// would imply that the component must be IMMUTABLE too
188+
// (see `compile-fail/borrowck-imm-field-imm-base.rs`).
189+
//
190+
// 3. mut required, const declared: this shouldn't really be
191+
// possible, since I don't think you can declare a const
192+
// field, but I guess if we DID permit such a declaration
193+
// it would be equivalent to the case above?
194+
//
195+
// 4. imm required, * declared: In this case, the base must be
196+
// immutable. This is true regardless of what was declared
197+
// for this subcomponent, this if the base is mutable, the
198+
// subcomponent must be mutable.
199+
// (see `compile-fail/borrowck-imm-field-mut-base.rs`).
200+
//
201+
// 5. const required, * declared: In this case, the base need
202+
// only be const, since we don't ultimately care whether
203+
// the subcomponent is mutable or not.
204+
let base_mutbl = match (req_mutbl, comp_mutbl) {
205+
(m_mutbl, m_mutbl) => m_const, // (1)
206+
(m_mutbl, _) => m_mutbl, // (2, 3)
207+
(m_imm, _) => m_imm, // (4)
208+
(m_const, _) => m_const // (5)
157209
};
158210

159211
do self.loan(cmt_base, base_mutbl).chain |_ok| {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
struct Foo {
2+
x: uint
3+
}
4+
5+
struct Bar {
6+
foo: Foo
7+
}
8+
9+
fn main() {
10+
let mut b = Bar { foo: Foo { x: 3 } };
11+
let p = &b; //~ NOTE prior loan as immutable granted here
12+
let q = &mut b.foo.x; //~ ERROR loan of mutable local variable as mutable conflicts with prior loan
13+
let r = &p.foo.x;
14+
io::println(fmt!("*r = %u", *r));
15+
*q += 1;
16+
io::println(fmt!("*r = %u", *r));
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct Foo {
2+
mut x: uint
3+
}
4+
5+
struct Bar {
6+
foo: Foo
7+
}
8+
9+
fn main() {
10+
let mut b = Bar { foo: Foo { x: 3 } };
11+
let p = &b.foo.x;
12+
let q = &mut b.foo; //~ ERROR loan of mutable field as mutable conflicts with prior loan
13+
//~^ ERROR loan of mutable local variable as mutable conflicts with prior loan
14+
let r = &mut b; //~ ERROR loan of mutable local variable as mutable conflicts with prior loan
15+
io::println(fmt!("*p = %u", *p));
16+
q.x += 1;
17+
r.foo.x += 1;
18+
io::println(fmt!("*p = %u", *p));
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
struct Foo {
2+
mut x: uint
3+
}
4+
5+
struct Bar {
6+
foo: Foo
7+
}
8+
9+
fn main() {
10+
let mut b = Bar { foo: Foo { x: 3 } };
11+
let p = &b;
12+
let q = &mut b.foo.x;
13+
let r = &p.foo.x; //~ ERROR illegal borrow unless pure
14+
let s = &b.foo.x; //~ ERROR loan of mutable field as immutable conflicts with prior loan
15+
io::println(fmt!("*r = %u", *r));
16+
io::println(fmt!("*r = %u", *s));
17+
*q += 1;
18+
io::println(fmt!("*r = %u", *r));
19+
io::println(fmt!("*r = %u", *s));
20+
}

0 commit comments

Comments
 (0)