Support unions in borrow checker

Add some more tests
This commit is contained in:
Vadim Petrochenkov 2016-08-26 16:54:58 +03:00
parent 59ccb7b6db
commit 5f975e969b
12 changed files with 484 additions and 11 deletions

View File

@ -461,6 +461,10 @@ fn add_fragment_siblings_for_extension<'a, 'tcx>(this: &MoveData<'tcx>,
}
}
(&ty::TyUnion(..), None) => {
// Do nothing, all union fields are moved/assigned together.
}
(&ty::TyEnum(def, _), ref enum_variant_info) => {
let variant = match *enum_variant_info {
Some((vid, ref _lp2)) => def.variant_with_id(vid),

View File

@ -89,7 +89,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
self.restrict(cmt_base)
}
Categorization::Interior(cmt_base, i) => {
Categorization::Interior(cmt_base, interior) => {
// R-Field
//
// Overwriting the base would not change the type of
@ -99,8 +99,34 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
Categorization::Downcast(_, variant_id) => Some(variant_id),
_ => None
};
let interior = interior.cleaned();
let base_ty = cmt_base.ty;
let result = self.restrict(cmt_base);
self.extend(result, &cmt, LpInterior(opt_variant_id, i.cleaned()))
if let ty::TyUnion(ref adt_def, _) = base_ty.sty {
match result {
RestrictionResult::Safe => RestrictionResult::Safe,
RestrictionResult::SafeIf(base_lp, mut base_vec) => {
for field in &adt_def.struct_variant().fields {
let field = InteriorKind::InteriorField(mc::NamedField(field.name));
let field_ty = if field == interior {
cmt.ty
} else {
self.bccx.tcx.types.err // Doesn't matter
};
let sibling_lp_kind = LpExtend(base_lp.clone(), cmt.mutbl,
LpInterior(opt_variant_id, field));
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
base_vec.push(sibling_lp);
}
let lp = new_lp(LpExtend(base_lp, cmt.mutbl,
LpInterior(opt_variant_id, interior)));
RestrictionResult::SafeIf(lp, base_vec)
}
}
} else {
self.extend(result, &cmt, LpInterior(opt_variant_id, interior))
}
}
Categorization::StaticItem => {

View File

@ -477,8 +477,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
base.common(&base2).map(|x| {
let xd = x.depth();
if base.depth() == xd && base2.depth() == xd {
assert_eq!(base.ty, base2.ty);
assert_eq!(self.ty, other.ty);
LoanPath {
kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
ty: self.ty,
@ -495,7 +493,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
(_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&other),
(&LpVar(id), &LpVar(id2)) => {
if id == id2 {
assert_eq!(self.ty, other.ty);
Some(LoanPath { kind: LpVar(id), ty: self.ty })
} else {
None
@ -503,7 +500,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
}
(&LpUpvar(id), &LpUpvar(id2)) => {
if id == id2 {
assert_eq!(self.ty, other.ty);
Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
} else {
None
@ -1136,7 +1132,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
out.push(')');
}
LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
self.append_autoderefd_loan_path_to_string(&lp_base, out);
match fname {

View File

@ -21,7 +21,8 @@ use rustc::middle::dataflow::DataFlowOperator;
use rustc::middle::dataflow::KillFrom;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::expr_use_visitor::MutateMode;
use rustc::ty::TyCtxt;
use rustc::middle::mem_categorization as mc;
use rustc::ty::{self, TyCtxt};
use rustc::util::nodemap::{FnvHashMap, NodeSet};
use std::cell::RefCell;
@ -364,6 +365,32 @@ impl<'a, 'tcx> MoveData<'tcx> {
lp: Rc<LoanPath<'tcx>>,
id: ast::NodeId,
kind: MoveKind) {
// Moving one union field automatically moves all its fields.
if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
if let ty::TyUnion(ref adt_def, _) = base_lp.ty.sty {
for field in &adt_def.struct_variant().fields {
let field = InteriorKind::InteriorField(mc::NamedField(field.name));
let field_ty = if field == interior {
lp.ty
} else {
tcx.types.err // Doesn't matter
};
let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
LpInterior(opt_variant_id, field));
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
self.add_move_helper(tcx, sibling_lp, id, kind);
}
return;
}
}
self.add_move_helper(tcx, lp.clone(), id, kind);
}
fn add_move_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
lp: Rc<LoanPath<'tcx>>,
id: ast::NodeId,
kind: MoveKind) {
debug!("add_move(lp={:?}, id={}, kind={:?})",
lp,
id,
@ -393,6 +420,34 @@ impl<'a, 'tcx> MoveData<'tcx> {
span: Span,
assignee_id: ast::NodeId,
mode: euv::MutateMode) {
// Assigning to one union field automatically assigns to all its fields.
if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
if let ty::TyUnion(ref adt_def, _) = base_lp.ty.sty {
for field in &adt_def.struct_variant().fields {
let field = InteriorKind::InteriorField(mc::NamedField(field.name));
let field_ty = if field == interior {
lp.ty
} else {
tcx.types.err // Doesn't matter
};
let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
LpInterior(opt_variant_id, field));
let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
self.add_assignment_helper(tcx, sibling_lp, assign_id, span, assignee_id, mode);
}
return;
}
}
self.add_assignment_helper(tcx, lp.clone(), assign_id, span, assignee_id, mode);
}
pub fn add_assignment_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
lp: Rc<LoanPath<'tcx>>,
assign_id: ast::NodeId,
span: Span,
assignee_id: ast::NodeId,
mode: euv::MutateMode) {
debug!("add_assignment(lp={:?}, assign_id={}, assignee_id={}",
lp, assign_id, assignee_id);

View File

@ -0,0 +1,44 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
#![feature(untagged_unions)]
#[derive(Clone, Copy)]
struct S {
a: u8,
b: u16,
}
union U {
s: S,
c: u32,
}
impl Clone for U {
fn clone(&self) -> Self { *self }
}
impl Copy for U {}
fn main() {
unsafe {
{
let mut u = U { s: S { a: 0, b: 1 } };
let ra = &mut u.s.a;
let b = u.s.b; // OK
}
{
let mut u = U { s: S { a: 0, b: 1 } };
let ra = &mut u.s.a;
let b = u.c; //~ ERROR cannot use `u.c` because it was mutably borrowed
}
}
}

View File

@ -0,0 +1,97 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
#![feature(untagged_unions)]
union U {
a: u8,
b: u64,
}
impl Clone for U {
fn clone(&self) -> Self { *self }
}
impl Copy for U {}
fn main() {
unsafe {
let mut u = U { b: 0 };
// Imm borrow, same field
{
let ra = &u.a;
let ra2 = &u.a; // OK
}
{
let ra = &u.a;
let a = u.a; // OK
}
{
let ra = &u.a;
let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
}
{
let ra = &u.a;
u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
}
// Imm borrow, other field
{
let ra = &u.a;
let rb = &u.b; // OK
}
{
let ra = &u.a;
let b = u.b; // OK
}
{
let ra = &u.a;
let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
}
{
let ra = &u.a;
u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
}
// Mut borrow, same field
{
let rma = &mut u.a;
let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
}
{
let ra = &mut u.a;
let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed
}
{
let rma = &mut u.a;
let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time
}
{
let rma = &mut u.a;
u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
}
// Mut borrow, other field
{
let rma = &mut u.a;
let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
}
{
let ra = &mut u.a;
let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed
}
{
let rma = &mut u.a;
let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
}
{
let rma = &mut u.a;
u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
}
}
}

View File

@ -0,0 +1,42 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(untagged_unions)]
// Non-copy
struct A;
struct B;
union U {
a: A,
b: B,
}
fn main() {
unsafe {
{
let mut u = U { a: A };
let a = u.a;
let a = u.a; //~ ERROR use of moved value: `u.a`
}
{
let mut u = U { a: A };
let a = u.a;
u.a = A;
let a = u.a; // OK
}
{
let mut u = U { a: A };
let a = u.a;
u.b = B;
let a = u.a; // OK
}
}
}

View File

@ -0,0 +1,96 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(untagged_unions)]
#[derive(Clone, Copy)]
struct Copy;
struct NonCopy;
union Unn {
n1: NonCopy,
n2: NonCopy,
}
union Ucc {
c1: Copy,
c2: Copy,
}
union Ucn {
c: Copy,
n: NonCopy,
}
fn main() {
unsafe {
// 2 NonCopy
{
let mut u = Unn { n1: NonCopy };
let a = u.n1;
let a = u.n1; //~ ERROR use of moved value: `u.n1`
}
{
let mut u = Unn { n1: NonCopy };
let a = u.n1;
let a = u; //~ ERROR use of partially moved value: `u`
}
{
let mut u = Unn { n1: NonCopy };
let a = u.n1;
let a = u.n2; //~ ERROR use of moved value: `u.n2`
}
// 2 Copy
{
let mut u = Ucc { c1: Copy };
let a = u.c1;
let a = u.c1; // OK
}
{
let mut u = Ucc { c1: Copy };
let a = u.c1;
let a = u; // OK
}
{
let mut u = Ucc { c1: Copy };
let a = u.c1;
let a = u.c2; // OK
}
// 1 Copy, 1 NonCopy
{
let mut u = Ucn { c: Copy };
let a = u.c;
let a = u.c; // OK
}
{
let mut u = Ucn { c: Copy };
let a = u.n;
let a = u.n; //~ ERROR use of moved value: `u.n`
}
{
let mut u = Ucn { c: Copy };
let a = u.n;
let a = u.c; //~ ERROR use of moved value: `u.c`
}
{
let mut u = Ucn { c: Copy };
let a = u.c;
let a = u.n; // OK
}
{
let mut u = Ucn { c: Copy };
let a = u.c;
let a = u; // OK
}
{
let mut u = Ucn { c: Copy };
let a = u.n;
let a = u; //~ ERROR use of partially moved value: `u`
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(untagged_unions)]
struct S {
a: u8,
}
union U {
a: u8,
}
fn main() {
unsafe {
let mut s: S;
let mut u: U;
s.a = 0;
u.a = 0;
let sa = s.a; //~ ERROR use of possibly uninitialized variable: `s.a`
let ua = u.a; //~ ERROR use of possibly uninitialized variable: `u.a`
}
}

View File

@ -52,8 +52,8 @@ fn main() {
unsafe {
assert_eq!(w.a, 0);
assert_eq!(w.b, 0);
// w.a = 1;
assert_eq!(w.a, 0);
assert_eq!(w.b, 0);
w.a = 1;
assert_eq!(w.a, 1);
assert_eq!(w.b, 1);
}
}

View File

@ -0,0 +1,44 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Drop works for union itself.
#![feature(untagged_unions)]
struct S;
union U {
a: S
}
impl Drop for S {
fn drop(&mut self) {
unsafe { CHECK += 10; }
}
}
impl Drop for U {
fn drop(&mut self) {
unsafe { CHECK += 1; }
}
}
static mut CHECK: u8 = 0;
fn main() {
unsafe {
let mut u = U { a: S };
assert_eq!(CHECK, 0);
u = U { a: S };
assert_eq!(CHECK, 1); // union itself is assigned, union is dropped, field is not dropped
u.a = S;
assert_eq!(CHECK, 11); // union field is assigned, field is dropped
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(core_float)]
#![feature(float_extras)]
#![feature(untagged_unions)]
extern crate core;
use core::num::Float;
union U {
a: (u8, u8),
b: u16,
}
union W {
a: u32,
b: f32,
}
fn main() {
unsafe {
let mut u = U { a: (1, 1) };
assert_eq!(u.b, (1 << 8) + 1);
u.b = (2 << 8) + 2;
assert_eq!(u.a, (2, 2));
let mut w = W { a: 0b0_11111111_00000000000000000000000 };
assert_eq!(w.b, f32::infinity());
w.b = f32::neg_infinity();
assert_eq!(w.a, 0b1_11111111_00000000000000000000000);
}
}