diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index 067140500102..66118dbc38c3 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -238,11 +238,13 @@ enum ptr_kind {uniq_ptr, gc_ptr, region_ptr, unsafe_ptr} // I am coining the term "components" to mean "pieces of a data // structure accessible without a dereference": enum comp_kind { - comp_tuple, comp_res, comp_variant, - comp_field(str, // name of field + comp_tuple, // elt in a tuple + comp_res, // data for a resource + comp_variant(ast::def_id), // internals to a variant of given enum + comp_field(str, // name of field ast::mutability), // declared mutability of field - comp_index(ty::t, // type of vec/str/etc being deref'd - ast::mutability) // mutability of vec content + comp_index(ty::t, // type of vec/str/etc being deref'd + ast::mutability) // mutability of vec content } // 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 { comp_index(*) { "[]" } comp_tuple { "()" } comp_res { "" } - comp_variant { "" } + comp_variant(_) { "" } } } @@ -468,7 +470,7 @@ impl to_str_methods for borrowck_ctxt { cat_comp(_, comp_field(*)) { mut_str + " field" } cat_comp(_, comp_tuple) { "tuple content" } cat_comp(_, comp_res) { "resource content" } - cat_comp(_, comp_variant) { "enum content" } + cat_comp(_, comp_variant(_)) { "enum content" } cat_comp(_, comp_index(t, _)) { alt ty::get(t).struct { ty::ty_vec(*) | ty::ty_evec(*) { @@ -514,7 +516,7 @@ impl to_str_methods for borrowck_ctxt { // mutable structure. fn inherent_mutability(ck: comp_kind) -> mutability { alt ck { - comp_tuple | comp_res | comp_variant {m_imm} + comp_tuple | comp_res | comp_variant(_) {m_imm} comp_field(_, m) | comp_index(_, m) {m} } } \ No newline at end of file diff --git a/src/rustc/middle/borrowck/categorization.rs b/src/rustc/middle/borrowck/categorization.rs index ff9d51196c91..db22f880941b 100644 --- a/src/rustc/middle/borrowck/categorization.rs +++ b/src/rustc/middle/borrowck/categorization.rs @@ -67,8 +67,8 @@ fn opt_deref_kind(t: ty::t) -> option { some(deref_ptr(unsafe_ptr)) } - ty::ty_enum(*) { - some(deref_comp(comp_variant)) + ty::ty_enum(did, _) { + some(deref_comp(comp_variant(did))) } ty::ty_res(*) { @@ -275,10 +275,12 @@ impl public_methods for borrowck_ctxt { } } - fn cat_variant(arg: N, cmt: cmt) -> cmt { + fn cat_variant(arg: N, + enum_did: ast::def_id, + cmt: cmt) -> cmt { @{id: arg.id(), span: arg.span(), - cat: cat_comp(cmt, comp_variant), - lp: cmt.lp.map { |l| @lp_comp(l, comp_variant) }, + cat: cat_comp(cmt, comp_variant(enum_did)), + lp: cmt.lp.map { |l| @lp_comp(l, comp_variant(enum_did)) }, mutbl: cmt.mutbl, // imm iff in an immutable context ty: self.tcx.ty(arg)} } diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs index ed261b09b73d..87980bf72481 100644 --- a/src/rustc/middle/borrowck/gather_loans.rs +++ b/src/rustc/middle/borrowck/gather_loans.rs @@ -338,8 +338,16 @@ impl methods for gather_loan_ctxt { } ast::pat_enum(_, some(subpats)) { // variant(x, y, z) + let enum_did = alt self.bccx.tcx.def_map +.find(pat.id) { + some(ast::def_variant(enum_did, _)) {enum_did} + e {tcx.sess.span_bug(pat.span, + #fmt["resolved to %?, \ + not variant", e])} + }; + for subpats.each { |subpat| - let subcmt = self.bccx.cat_variant(subpat, cmt); + let subcmt = self.bccx.cat_variant(subpat, enum_did, cmt); self.gather_pat(subcmt, subpat, arm_id, alt_id); } } diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs index c0e6f4accf70..ee61dd9f0cf6 100644 --- a/src/rustc/middle/borrowck/loan.rs +++ b/src/rustc/middle/borrowck/loan.rs @@ -65,24 +65,23 @@ impl loan_methods for loan_ctxt { // that case, it must also be embedded in an immutable // location, or else the whole structure could be // overwritten and the component along with it. - let base_mutbl = alt req_mutbl { - m_imm { m_imm } - m_const | m_mutbl { m_const } - }; - - self.loan(cmt_base, base_mutbl); - self.ok_with_loan_of(cmt, req_mutbl) + self.loan_stable_comp(cmt, cmt_base, req_mutbl) } - cat_comp(cmt1, comp_variant) | - cat_deref(cmt1, _, uniq_ptr) { - // Variant components: the base must be immutable, because - // if it is overwritten, the types of the embedded data - // could change. - // - // Unique pointers: the base must be immutable, because if - // it is overwritten, the unique content will be freed. - self.loan(cmt1, m_imm); - self.ok_with_loan_of(cmt, req_mutbl) + cat_comp(cmt_base, comp_variant(enum_did)) { + // For enums, the memory is unstable if there are multiple + // variants, because if the enum value is overwritten then + // the memory changes type. + if ty::enum_is_univariant(self.bccx.tcx, enum_did) { + self.loan_stable_comp(cmt, cmt_base, req_mutbl) + } else { + self.loan_unstable_deref(cmt, cmt_base, req_mutbl) + } + } + cat_deref(cmt_base, _, uniq_ptr) { + // For unique pointers, the memory being pointed out is + // unstable because if the unique pointer is overwritten + // then the memory is freed. + self.loan_unstable_deref(cmt, cmt_base, req_mutbl) } cat_deref(cmt1, _, unsafe_ptr) | cat_deref(cmt1, _, gc_ptr) | @@ -94,4 +93,32 @@ impl loan_methods for loan_ctxt { } } } + + // A "stable component" is one where assigning the base of the + // component cannot cause the component itself to change types. + // Example: record fields. + fn loan_stable_comp(cmt: cmt, + cmt_base: cmt, + req_mutbl: ast::mutability) { + let base_mutbl = alt req_mutbl { + m_imm { m_imm } + m_const | m_mutbl { m_const } + }; + + self.loan(cmt_base, base_mutbl); + self.ok_with_loan_of(cmt, req_mutbl) + } + + // An "unstable deref" means a deref of a ptr/comp where, if the + // base of the deref is assigned to, pointers into the result of the + // deref would be invalidated. Examples: interior of variants, uniques. + fn loan_unstable_deref(cmt: cmt, + cmt_base: cmt, + req_mutbl: ast::mutability) { + // Variant components: the base must be immutable, because + // if it is overwritten, the types of the embedded data + // could change. + self.loan(cmt_base, m_imm); + self.ok_with_loan_of(cmt, req_mutbl) + } } diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs index 441dfad9c2d5..d4e2b3cbe60f 100644 --- a/src/rustc/middle/borrowck/preserve.rs +++ b/src/rustc/middle/borrowck/preserve.rs @@ -43,10 +43,19 @@ impl public_methods for borrowck_ctxt { // type never changes. self.preserve(cmt_base, opt_scope_id) } - cat_comp(cmt_base, comp_variant) { - self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_variant) + cat_comp(cmt_base, comp_variant(enum_did)) { + if ty::enum_is_univariant(self.tcx, enum_did) { + self.preserve(cmt_base, opt_scope_id) + } else { + // If there are multiple variants: overwriting the + // base could cause the type of this memory to change, + // so require imm. + self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_variant) + } } cat_deref(cmt_base, _, uniq_ptr) { + // Overwriting the base could cause this memory to be + // freed, so require imm. self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_uniq) } cat_deref(_, _, region_ptr) { diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index ca88ea927e2f..972c6c7e708a 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -68,7 +68,7 @@ export sty; export subst, subst_tps, substs_is_noop, substs_to_str, substs; export t; export new_ty_hash; -export enum_variants, substd_enum_variants; +export enum_variants, substd_enum_variants, enum_is_univariant; export iface_methods, store_iface_methods, impl_iface; export enum_variant_with_id; export ty_dtor; @@ -2663,6 +2663,10 @@ fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path { } } +fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool { + vec::len(*enum_variants(cx, id)) == 1u +} + fn enum_variants(cx: ctxt, id: ast::def_id) -> @[variant_info] { alt cx.enum_var_cache.find(id) { some(variants) { ret variants; } diff --git a/src/test/run-pass/borrowck-newtype-issue-2573.rs b/src/test/run-pass/borrowck-newtype-issue-2573.rs new file mode 100644 index 000000000000..5ca39da8cb3c --- /dev/null +++ b/src/test/run-pass/borrowck-newtype-issue-2573.rs @@ -0,0 +1,16 @@ +enum foo = {mut bar: baz}; + +enum baz = @{mut baz: int}; + +impl quuux for foo { + fn frob() { + really_impure(self.bar); + } +} + +// Override default mode so that we are passing by value +fn really_impure(++bar: baz) { + bar.baz = 3; +} + +fn main() {} \ No newline at end of file diff --git a/src/test/run-pass/borrowck-univariant-enum.rs b/src/test/run-pass/borrowck-univariant-enum.rs new file mode 100644 index 000000000000..3b01c4aa076b --- /dev/null +++ b/src/test/run-pass/borrowck-univariant-enum.rs @@ -0,0 +1,19 @@ +enum newtype { + newtype(int) +} + +fn main() { + + // Test that borrowck treats enums with a single variant + // specially. + + let x = @mut 5; + let y = @mut newtype(3); + let z = alt *y { + newtype(b) { + *x += 1; + *x * b + } + }; + assert z == 18; +} \ No newline at end of file