Skip to content

Commit 5f975e9

Browse files
committed
Support unions in borrow checker
Add some more tests
1 parent 59ccb7b commit 5f975e9

12 files changed

+484
-11
lines changed

src/librustc_borrowck/borrowck/fragments.rs

+4
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,10 @@ fn add_fragment_siblings_for_extension<'a, 'tcx>(this: &MoveData<'tcx>,
461461
}
462462
}
463463

464+
(&ty::TyUnion(..), None) => {
465+
// Do nothing, all union fields are moved/assigned together.
466+
}
467+
464468
(&ty::TyEnum(def, _), ref enum_variant_info) => {
465469
let variant = match *enum_variant_info {
466470
Some((vid, ref _lp2)) => def.variant_with_id(vid),

src/librustc_borrowck/borrowck/gather_loans/restrictions.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
8989
self.restrict(cmt_base)
9090
}
9191

92-
Categorization::Interior(cmt_base, i) => {
92+
Categorization::Interior(cmt_base, interior) => {
9393
// R-Field
9494
//
9595
// Overwriting the base would not change the type of
@@ -99,8 +99,34 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
9999
Categorization::Downcast(_, variant_id) => Some(variant_id),
100100
_ => None
101101
};
102+
let interior = interior.cleaned();
103+
let base_ty = cmt_base.ty;
102104
let result = self.restrict(cmt_base);
103-
self.extend(result, &cmt, LpInterior(opt_variant_id, i.cleaned()))
105+
if let ty::TyUnion(ref adt_def, _) = base_ty.sty {
106+
match result {
107+
RestrictionResult::Safe => RestrictionResult::Safe,
108+
RestrictionResult::SafeIf(base_lp, mut base_vec) => {
109+
for field in &adt_def.struct_variant().fields {
110+
let field = InteriorKind::InteriorField(mc::NamedField(field.name));
111+
let field_ty = if field == interior {
112+
cmt.ty
113+
} else {
114+
self.bccx.tcx.types.err // Doesn't matter
115+
};
116+
let sibling_lp_kind = LpExtend(base_lp.clone(), cmt.mutbl,
117+
LpInterior(opt_variant_id, field));
118+
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
119+
base_vec.push(sibling_lp);
120+
}
121+
122+
let lp = new_lp(LpExtend(base_lp, cmt.mutbl,
123+
LpInterior(opt_variant_id, interior)));
124+
RestrictionResult::SafeIf(lp, base_vec)
125+
}
126+
}
127+
} else {
128+
self.extend(result, &cmt, LpInterior(opt_variant_id, interior))
129+
}
104130
}
105131

106132
Categorization::StaticItem => {

src/librustc_borrowck/borrowck/mod.rs

-5
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
477477
base.common(&base2).map(|x| {
478478
let xd = x.depth();
479479
if base.depth() == xd && base2.depth() == xd {
480-
assert_eq!(base.ty, base2.ty);
481-
assert_eq!(self.ty, other.ty);
482480
LoanPath {
483481
kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
484482
ty: self.ty,
@@ -495,15 +493,13 @@ impl<'a, 'tcx> LoanPath<'tcx> {
495493
(_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&other),
496494
(&LpVar(id), &LpVar(id2)) => {
497495
if id == id2 {
498-
assert_eq!(self.ty, other.ty);
499496
Some(LoanPath { kind: LpVar(id), ty: self.ty })
500497
} else {
501498
None
502499
}
503500
}
504501
(&LpUpvar(id), &LpUpvar(id2)) => {
505502
if id == id2 {
506-
assert_eq!(self.ty, other.ty);
507503
Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
508504
} else {
509505
None
@@ -1136,7 +1132,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
11361132
out.push(')');
11371133
}
11381134

1139-
11401135
LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
11411136
self.append_autoderefd_loan_path_to_string(&lp_base, out);
11421137
match fname {

src/librustc_borrowck/borrowck/move_data.rs

+56-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use rustc::middle::dataflow::DataFlowOperator;
2121
use rustc::middle::dataflow::KillFrom;
2222
use rustc::middle::expr_use_visitor as euv;
2323
use rustc::middle::expr_use_visitor::MutateMode;
24-
use rustc::ty::TyCtxt;
24+
use rustc::middle::mem_categorization as mc;
25+
use rustc::ty::{self, TyCtxt};
2526
use rustc::util::nodemap::{FnvHashMap, NodeSet};
2627

2728
use std::cell::RefCell;
@@ -364,6 +365,32 @@ impl<'a, 'tcx> MoveData<'tcx> {
364365
lp: Rc<LoanPath<'tcx>>,
365366
id: ast::NodeId,
366367
kind: MoveKind) {
368+
// Moving one union field automatically moves all its fields.
369+
if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
370+
if let ty::TyUnion(ref adt_def, _) = base_lp.ty.sty {
371+
for field in &adt_def.struct_variant().fields {
372+
let field = InteriorKind::InteriorField(mc::NamedField(field.name));
373+
let field_ty = if field == interior {
374+
lp.ty
375+
} else {
376+
tcx.types.err // Doesn't matter
377+
};
378+
let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
379+
LpInterior(opt_variant_id, field));
380+
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
381+
self.add_move_helper(tcx, sibling_lp, id, kind);
382+
}
383+
return;
384+
}
385+
}
386+
387+
self.add_move_helper(tcx, lp.clone(), id, kind);
388+
}
389+
390+
fn add_move_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
391+
lp: Rc<LoanPath<'tcx>>,
392+
id: ast::NodeId,
393+
kind: MoveKind) {
367394
debug!("add_move(lp={:?}, id={}, kind={:?})",
368395
lp,
369396
id,
@@ -393,6 +420,34 @@ impl<'a, 'tcx> MoveData<'tcx> {
393420
span: Span,
394421
assignee_id: ast::NodeId,
395422
mode: euv::MutateMode) {
423+
// Assigning to one union field automatically assigns to all its fields.
424+
if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
425+
if let ty::TyUnion(ref adt_def, _) = base_lp.ty.sty {
426+
for field in &adt_def.struct_variant().fields {
427+
let field = InteriorKind::InteriorField(mc::NamedField(field.name));
428+
let field_ty = if field == interior {
429+
lp.ty
430+
} else {
431+
tcx.types.err // Doesn't matter
432+
};
433+
let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
434+
LpInterior(opt_variant_id, field));
435+
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
436+
self.add_assignment_helper(tcx, sibling_lp, assign_id, span, assignee_id, mode);
437+
}
438+
return;
439+
}
440+
}
441+
442+
self.add_assignment_helper(tcx, lp.clone(), assign_id, span, assignee_id, mode);
443+
}
444+
445+
pub fn add_assignment_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
446+
lp: Rc<LoanPath<'tcx>>,
447+
assign_id: ast::NodeId,
448+
span: Span,
449+
assignee_id: ast::NodeId,
450+
mode: euv::MutateMode) {
396451
debug!("add_assignment(lp={:?}, assign_id={}, assignee_id={}",
397452
lp, assign_id, assignee_id);
398453

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-tidy-linelength
12+
13+
#![feature(untagged_unions)]
14+
15+
#[derive(Clone, Copy)]
16+
struct S {
17+
a: u8,
18+
b: u16,
19+
}
20+
21+
union U {
22+
s: S,
23+
c: u32,
24+
}
25+
26+
impl Clone for U {
27+
fn clone(&self) -> Self { *self }
28+
}
29+
impl Copy for U {}
30+
31+
fn main() {
32+
unsafe {
33+
{
34+
let mut u = U { s: S { a: 0, b: 1 } };
35+
let ra = &mut u.s.a;
36+
let b = u.s.b; // OK
37+
}
38+
{
39+
let mut u = U { s: S { a: 0, b: 1 } };
40+
let ra = &mut u.s.a;
41+
let b = u.c; //~ ERROR cannot use `u.c` because it was mutably borrowed
42+
}
43+
}
44+
}

src/test/compile-fail/union-borrow.rs

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-tidy-linelength
12+
13+
#![feature(untagged_unions)]
14+
15+
union U {
16+
a: u8,
17+
b: u64,
18+
}
19+
20+
impl Clone for U {
21+
fn clone(&self) -> Self { *self }
22+
}
23+
impl Copy for U {}
24+
25+
fn main() {
26+
unsafe {
27+
let mut u = U { b: 0 };
28+
// Imm borrow, same field
29+
{
30+
let ra = &u.a;
31+
let ra2 = &u.a; // OK
32+
}
33+
{
34+
let ra = &u.a;
35+
let a = u.a; // OK
36+
}
37+
{
38+
let ra = &u.a;
39+
let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
40+
}
41+
{
42+
let ra = &u.a;
43+
u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
44+
}
45+
// Imm borrow, other field
46+
{
47+
let ra = &u.a;
48+
let rb = &u.b; // OK
49+
}
50+
{
51+
let ra = &u.a;
52+
let b = u.b; // OK
53+
}
54+
{
55+
let ra = &u.a;
56+
let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
57+
}
58+
{
59+
let ra = &u.a;
60+
u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
61+
}
62+
// Mut borrow, same field
63+
{
64+
let rma = &mut u.a;
65+
let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
66+
}
67+
{
68+
let ra = &mut u.a;
69+
let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed
70+
}
71+
{
72+
let rma = &mut u.a;
73+
let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time
74+
}
75+
{
76+
let rma = &mut u.a;
77+
u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
78+
}
79+
// Mut borrow, other field
80+
{
81+
let rma = &mut u.a;
82+
let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
83+
}
84+
{
85+
let ra = &mut u.a;
86+
let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed
87+
}
88+
{
89+
let rma = &mut u.a;
90+
let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
91+
}
92+
{
93+
let rma = &mut u.a;
94+
u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
95+
}
96+
}
97+
}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(untagged_unions)]
12+
13+
// Non-copy
14+
struct A;
15+
struct B;
16+
17+
union U {
18+
a: A,
19+
b: B,
20+
}
21+
22+
fn main() {
23+
unsafe {
24+
{
25+
let mut u = U { a: A };
26+
let a = u.a;
27+
let a = u.a; //~ ERROR use of moved value: `u.a`
28+
}
29+
{
30+
let mut u = U { a: A };
31+
let a = u.a;
32+
u.a = A;
33+
let a = u.a; // OK
34+
}
35+
{
36+
let mut u = U { a: A };
37+
let a = u.a;
38+
u.b = B;
39+
let a = u.a; // OK
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)