Skip to content

Commit d1ec1d4

Browse files
committed
Treat enums with one variant specially in borrowck: rust-lang#2573
1 parent 1655c1a commit d1ec1d4

File tree

8 files changed

+120
-33
lines changed

8 files changed

+120
-33
lines changed

src/rustc/middle/borrowck.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,13 @@ enum ptr_kind {uniq_ptr, gc_ptr, region_ptr, unsafe_ptr}
238238
// I am coining the term "components" to mean "pieces of a data
239239
// structure accessible without a dereference":
240240
enum comp_kind {
241-
comp_tuple, comp_res, comp_variant,
242-
comp_field(str, // name of field
241+
comp_tuple, // elt in a tuple
242+
comp_res, // data for a resource
243+
comp_variant(ast::def_id), // internals to a variant of given enum
244+
comp_field(str, // name of field
243245
ast::mutability), // declared mutability of field
244-
comp_index(ty::t, // type of vec/str/etc being deref'd
245-
ast::mutability) // mutability of vec content
246+
comp_index(ty::t, // type of vec/str/etc being deref'd
247+
ast::mutability) // mutability of vec content
246248
}
247249

248250
// We pun on *T to mean both actual deref of a ptr as well
@@ -411,7 +413,7 @@ impl to_str_methods for borrowck_ctxt {
411413
comp_index(*) { "[]" }
412414
comp_tuple { "()" }
413415
comp_res { "<res>" }
414-
comp_variant { "<enum>" }
416+
comp_variant(_) { "<enum>" }
415417
}
416418
}
417419

@@ -468,7 +470,7 @@ impl to_str_methods for borrowck_ctxt {
468470
cat_comp(_, comp_field(*)) { mut_str + " field" }
469471
cat_comp(_, comp_tuple) { "tuple content" }
470472
cat_comp(_, comp_res) { "resource content" }
471-
cat_comp(_, comp_variant) { "enum content" }
473+
cat_comp(_, comp_variant(_)) { "enum content" }
472474
cat_comp(_, comp_index(t, _)) {
473475
alt ty::get(t).struct {
474476
ty::ty_vec(*) | ty::ty_evec(*) {
@@ -514,7 +516,7 @@ impl to_str_methods for borrowck_ctxt {
514516
// mutable structure.
515517
fn inherent_mutability(ck: comp_kind) -> mutability {
516518
alt ck {
517-
comp_tuple | comp_res | comp_variant {m_imm}
519+
comp_tuple | comp_res | comp_variant(_) {m_imm}
518520
comp_field(_, m) | comp_index(_, m) {m}
519521
}
520522
}

src/rustc/middle/borrowck/categorization.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ fn opt_deref_kind(t: ty::t) -> option<deref_kind> {
6767
some(deref_ptr(unsafe_ptr))
6868
}
6969

70-
ty::ty_enum(*) {
71-
some(deref_comp(comp_variant))
70+
ty::ty_enum(did, _) {
71+
some(deref_comp(comp_variant(did)))
7272
}
7373

7474
ty::ty_res(*) {
@@ -275,10 +275,12 @@ impl public_methods for borrowck_ctxt {
275275
}
276276
}
277277

278-
fn cat_variant<N: ast_node>(arg: N, cmt: cmt) -> cmt {
278+
fn cat_variant<N: ast_node>(arg: N,
279+
enum_did: ast::def_id,
280+
cmt: cmt) -> cmt {
279281
@{id: arg.id(), span: arg.span(),
280-
cat: cat_comp(cmt, comp_variant),
281-
lp: cmt.lp.map { |l| @lp_comp(l, comp_variant) },
282+
cat: cat_comp(cmt, comp_variant(enum_did)),
283+
lp: cmt.lp.map { |l| @lp_comp(l, comp_variant(enum_did)) },
282284
mutbl: cmt.mutbl, // imm iff in an immutable context
283285
ty: self.tcx.ty(arg)}
284286
}

src/rustc/middle/borrowck/gather_loans.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,16 @@ impl methods for gather_loan_ctxt {
338338
}
339339
ast::pat_enum(_, some(subpats)) {
340340
// variant(x, y, z)
341+
let enum_did = alt self.bccx.tcx.def_map
342+
.find(pat.id) {
343+
some(ast::def_variant(enum_did, _)) {enum_did}
344+
e {tcx.sess.span_bug(pat.span,
345+
#fmt["resolved to %?, \
346+
not variant", e])}
347+
};
348+
341349
for subpats.each { |subpat|
342-
let subcmt = self.bccx.cat_variant(subpat, cmt);
350+
let subcmt = self.bccx.cat_variant(subpat, enum_did, cmt);
343351
self.gather_pat(subcmt, subpat, arm_id, alt_id);
344352
}
345353
}

src/rustc/middle/borrowck/loan.rs

+44-17
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,23 @@ impl loan_methods for loan_ctxt {
6565
// that case, it must also be embedded in an immutable
6666
// location, or else the whole structure could be
6767
// overwritten and the component along with it.
68-
let base_mutbl = alt req_mutbl {
69-
m_imm { m_imm }
70-
m_const | m_mutbl { m_const }
71-
};
72-
73-
self.loan(cmt_base, base_mutbl);
74-
self.ok_with_loan_of(cmt, req_mutbl)
68+
self.loan_stable_comp(cmt, cmt_base, req_mutbl)
7569
}
76-
cat_comp(cmt1, comp_variant) |
77-
cat_deref(cmt1, _, uniq_ptr) {
78-
// Variant components: the base must be immutable, because
79-
// if it is overwritten, the types of the embedded data
80-
// could change.
81-
//
82-
// Unique pointers: the base must be immutable, because if
83-
// it is overwritten, the unique content will be freed.
84-
self.loan(cmt1, m_imm);
85-
self.ok_with_loan_of(cmt, req_mutbl)
70+
cat_comp(cmt_base, comp_variant(enum_did)) {
71+
// For enums, the memory is unstable if there are multiple
72+
// variants, because if the enum value is overwritten then
73+
// the memory changes type.
74+
if ty::enum_is_univariant(self.bccx.tcx, enum_did) {
75+
self.loan_stable_comp(cmt, cmt_base, req_mutbl)
76+
} else {
77+
self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
78+
}
79+
}
80+
cat_deref(cmt_base, _, uniq_ptr) {
81+
// For unique pointers, the memory being pointed out is
82+
// unstable because if the unique pointer is overwritten
83+
// then the memory is freed.
84+
self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
8685
}
8786
cat_deref(cmt1, _, unsafe_ptr) |
8887
cat_deref(cmt1, _, gc_ptr) |
@@ -94,4 +93,32 @@ impl loan_methods for loan_ctxt {
9493
}
9594
}
9695
}
96+
97+
// A "stable component" is one where assigning the base of the
98+
// component cannot cause the component itself to change types.
99+
// Example: record fields.
100+
fn loan_stable_comp(cmt: cmt,
101+
cmt_base: cmt,
102+
req_mutbl: ast::mutability) {
103+
let base_mutbl = alt req_mutbl {
104+
m_imm { m_imm }
105+
m_const | m_mutbl { m_const }
106+
};
107+
108+
self.loan(cmt_base, base_mutbl);
109+
self.ok_with_loan_of(cmt, req_mutbl)
110+
}
111+
112+
// An "unstable deref" means a deref of a ptr/comp where, if the
113+
// base of the deref is assigned to, pointers into the result of the
114+
// deref would be invalidated. Examples: interior of variants, uniques.
115+
fn loan_unstable_deref(cmt: cmt,
116+
cmt_base: cmt,
117+
req_mutbl: ast::mutability) {
118+
// Variant components: the base must be immutable, because
119+
// if it is overwritten, the types of the embedded data
120+
// could change.
121+
self.loan(cmt_base, m_imm);
122+
self.ok_with_loan_of(cmt, req_mutbl)
123+
}
97124
}

src/rustc/middle/borrowck/preserve.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,19 @@ impl public_methods for borrowck_ctxt {
4343
// type never changes.
4444
self.preserve(cmt_base, opt_scope_id)
4545
}
46-
cat_comp(cmt_base, comp_variant) {
47-
self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_variant)
46+
cat_comp(cmt_base, comp_variant(enum_did)) {
47+
if ty::enum_is_univariant(self.tcx, enum_did) {
48+
self.preserve(cmt_base, opt_scope_id)
49+
} else {
50+
// If there are multiple variants: overwriting the
51+
// base could cause the type of this memory to change,
52+
// so require imm.
53+
self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_variant)
54+
}
4855
}
4956
cat_deref(cmt_base, _, uniq_ptr) {
57+
// Overwriting the base could cause this memory to be
58+
// freed, so require imm.
5059
self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_uniq)
5160
}
5261
cat_deref(_, _, region_ptr) {

src/rustc/middle/ty.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export sty;
6868
export subst, subst_tps, substs_is_noop, substs_to_str, substs;
6969
export t;
7070
export new_ty_hash;
71-
export enum_variants, substd_enum_variants;
71+
export enum_variants, substd_enum_variants, enum_is_univariant;
7272
export iface_methods, store_iface_methods, impl_iface;
7373
export enum_variant_with_id;
7474
export ty_dtor;
@@ -2663,6 +2663,10 @@ fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path {
26632663
}
26642664
}
26652665

2666+
fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool {
2667+
vec::len(*enum_variants(cx, id)) == 1u
2668+
}
2669+
26662670
fn enum_variants(cx: ctxt, id: ast::def_id) -> @[variant_info] {
26672671
alt cx.enum_var_cache.find(id) {
26682672
some(variants) { ret variants; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
enum foo = {mut bar: baz};
2+
3+
enum baz = @{mut baz: int};
4+
5+
impl quuux for foo {
6+
fn frob() {
7+
really_impure(self.bar);
8+
}
9+
}
10+
11+
// Override default mode so that we are passing by value
12+
fn really_impure(++bar: baz) {
13+
bar.baz = 3;
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enum newtype {
2+
newtype(int)
3+
}
4+
5+
fn main() {
6+
7+
// Test that borrowck treats enums with a single variant
8+
// specially.
9+
10+
let x = @mut 5;
11+
let y = @mut newtype(3);
12+
let z = alt *y {
13+
newtype(b) {
14+
*x += 1;
15+
*x * b
16+
}
17+
};
18+
assert z == 18;
19+
}

0 commit comments

Comments
 (0)