Skip to content

Commit c55200a

Browse files
committedNov 19, 2017
Handle borrows of array elements
1 parent 6160040 commit c55200a

File tree

8 files changed

+187
-116
lines changed

8 files changed

+187
-116
lines changed
 

‎src/librustc_mir/borrow_check.rs

Lines changed: 89 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use dataflow::{MovingOutStatements};
3535
use dataflow::{Borrows, BorrowData, BorrowIndex};
3636
use dataflow::move_paths::{MoveError, IllegalMoveOriginKind};
3737
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult, MoveOutIndex};
38+
use dataflow::abs_domain::Lift;
3839
use util::borrowck_errors::{BorrowckErrors, Origin};
3940

4041
use self::MutateMode::{JustWrite, WriteAndRead};
@@ -841,15 +842,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
841842
-> Result<MovePathIndex, NoMovePathFound>
842843
{
843844
let mut last_prefix = lvalue;
844-
for prefix in self.prefixes(lvalue, PrefixSet::All) {
845+
for prefix in self.prefixes(lvalue) {
845846
if let Some(mpi) = self.move_path_for_lvalue(prefix) {
846847
return Ok(mpi);
847848
}
848849
last_prefix = prefix;
849850
}
850851
match *last_prefix {
851852
Lvalue::Local(_) => panic!("should have move path for every Local"),
852-
Lvalue::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"),
853+
Lvalue::Projection(_) => panic!("`prefixes` meant dont stop for Projection"),
853854
Lvalue::Static(_) => return Err(NoMovePathFound::ReachedStatic),
854855
}
855856
}
@@ -1093,6 +1094,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10931094
where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue<'tcx>) -> Control
10941095
{
10951096
let (access, lvalue) = access_lvalue;
1097+
debug!(
1098+
"each_borrow_involving_path({:?}, {:?})",
1099+
access,
1100+
self.describe_lvalue(lvalue)
1101+
);
1102+
1103+
let mut ps = vec![];
1104+
let mut base = lvalue;
1105+
while let Lvalue::Projection(ref proj) = *base {
1106+
ps.push(proj.elem.lift());
1107+
base = &proj.base;
1108+
}
1109+
debug!("base = {:?}, projections = {:?}", base, ps);
10961110

10971111
// FIXME: analogous code in check_loans first maps `lvalue` to
10981112
// its base_path.
@@ -1105,49 +1119,84 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11051119
'next_borrow: for i in flow_state.borrows.elems_incoming() {
11061120
let borrowed = &data[i];
11071121

1108-
// Is `lvalue` (or a prefix of it) already borrowed? If
1109-
// so, that's relevant.
1110-
//
1122+
debug!("borrowed = {:?}", borrowed);
1123+
11111124
// FIXME: Differs from AST-borrowck; includes drive-by fix
11121125
// to #38899. Will probably need back-compat mode flag.
1113-
for accessed_prefix in self.prefixes(lvalue, PrefixSet::All) {
1114-
if *accessed_prefix == borrowed.lvalue {
1126+
if base == &borrowed.base_lvalue {
1127+
let bps = &borrowed.projections;
1128+
1129+
// Is `lvalue` (or a prefix of it) already borrowed? If
1130+
// so, that's relevant.
1131+
if ps.ends_with(bps) {
1132+
let accessed_prefix = {
1133+
let mut lv = lvalue;
1134+
for _ in 0..ps.len() - bps.len() {
1135+
if let Lvalue::Projection(ref proj) = *lv {
1136+
lv = &proj.base;
1137+
} else {
1138+
unreachable!()
1139+
}
1140+
}
1141+
lv
1142+
};
1143+
debug!("accessed_prefix = {:?}", accessed_prefix);
11151144
// FIXME: pass in enum describing case we are in?
11161145
let ctrl = op(self, i, borrowed, accessed_prefix);
1117-
if ctrl == Control::Break { return; }
1146+
if ctrl == Control::Break {
1147+
return;
1148+
}
11181149
}
1119-
}
11201150

1121-
// Is `lvalue` a prefix (modulo access type) of the
1122-
// `borrowed.lvalue`? If so, that's relevant.
1123-
1124-
let prefix_kind = match access {
1125-
Shallow(Some(ArtificialField::Discriminant)) |
1126-
Shallow(Some(ArtificialField::ArrayLength)) => {
1127-
// The discriminant and array length are like
1128-
// additional fields on the type; they do not
1129-
// overlap any existing data there. Furthermore,
1130-
// they cannot actually be a prefix of any
1131-
// borrowed lvalue (at least in MIR as it is
1132-
// currently.)
1133-
continue 'next_borrow;
1134-
}
1135-
Shallow(None) => PrefixSet::Shallow,
1136-
Deep => PrefixSet::Supporting,
1137-
};
1151+
// Is `lvalue` a prefix (modulo access type) of the
1152+
// `borrowed.lvalue`? If so, that's relevant.
1153+
1154+
let shallow = match access {
1155+
Shallow(Some(ArtificialField::Discriminant)) |
1156+
Shallow(Some(ArtificialField::ArrayLength)) => {
1157+
// The discriminant and array length are like
1158+
// additional fields on the type; they do not
1159+
// overlap any existing data there. Furthermore,
1160+
// they cannot actually be a prefix of any
1161+
// borrowed lvalue (at least in MIR as it is
1162+
// currently.)
1163+
continue 'next_borrow;
1164+
}
1165+
Shallow(None) => true,
1166+
Deep => false,
1167+
};
1168+
let bps = if shallow {
1169+
&bps[borrowed.shallow_projections_len..]
1170+
} else {
1171+
bps
1172+
};
11381173

1139-
for borrowed_prefix in self.prefixes(&borrowed.lvalue, prefix_kind) {
1140-
if borrowed_prefix == lvalue {
1174+
if bps.len() != ps.len() &&
1175+
bps.ends_with(&ps)
1176+
{
1177+
let borrowed_prefix = {
1178+
let mut lv = &borrowed.lvalue;
1179+
for _ in 0..bps.len() - ps.len() {
1180+
if let Lvalue::Projection(ref proj) = *lv {
1181+
lv = &proj.base;
1182+
} else {
1183+
unreachable!()
1184+
}
1185+
}
1186+
lv
1187+
};
1188+
debug!("borrowed_prefix = {:?}", borrowed_prefix);
11411189
// FIXME: pass in enum describing case we are in?
11421190
let ctrl = op(self, i, borrowed, borrowed_prefix);
1143-
if ctrl == Control::Break { return; }
1191+
if ctrl == Control::Break {
1192+
return;
1193+
}
11441194
}
11451195
}
11461196
}
11471197
}
11481198
}
11491199

1150-
use self::prefixes::PrefixSet;
11511200

11521201
/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
11531202
/// lvalue are formed by stripping away fields and derefs, except that
@@ -1158,11 +1207,9 @@ use self::prefixes::PrefixSet;
11581207
/// is borrowed. But: writing `a` is legal if `*a` is borrowed,
11591208
/// whether or not `a` is a shared or mutable reference. [...] "
11601209
mod prefixes {
1161-
use super::{MirBorrowckCtxt};
1210+
use super::MirBorrowckCtxt;
11621211

1163-
use rustc::hir;
1164-
use rustc::ty::{self, TyCtxt};
1165-
use rustc::mir::{Lvalue, Mir, ProjectionElem};
1212+
use rustc::mir::{Lvalue, ProjectionElem};
11661213

11671214
pub trait IsPrefixOf<'tcx> {
11681215
fn is_prefix_of(&self, other: &Lvalue<'tcx>) -> bool;
@@ -1188,38 +1235,23 @@ mod prefixes {
11881235
}
11891236

11901237

1191-
pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
1192-
mir: &'cx Mir<'tcx>,
1193-
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
1194-
kind: PrefixSet,
1238+
pub(super) struct Prefixes<'cx, 'tcx: 'cx> {
11951239
next: Option<&'cx Lvalue<'tcx>>,
11961240
}
11971241

1198-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1199-
pub(super) enum PrefixSet {
1200-
/// Doesn't stop until it returns the base case (a Local or
1201-
/// Static prefix).
1202-
All,
1203-
/// Stops at any dereference.
1204-
Shallow,
1205-
/// Stops at the deref of a shared reference.
1206-
Supporting,
1207-
}
1208-
12091242
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12101243
/// Returns an iterator over the prefixes of `lvalue`
12111244
/// (inclusive) from longest to smallest, potentially
12121245
/// terminating the iteration early based on `kind`.
12131246
pub(super) fn prefixes(&self,
1214-
lvalue: &'cx Lvalue<'tcx>,
1215-
kind: PrefixSet)
1216-
-> Prefixes<'cx, 'gcx, 'tcx>
1247+
lvalue: &'cx Lvalue<'tcx>)
1248+
-> Prefixes<'cx, 'tcx>
12171249
{
1218-
Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
1250+
Prefixes { next: Some(lvalue) }
12191251
}
12201252
}
12211253

1222-
impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
1254+
impl<'cx, 'tcx: 'cx> Iterator for Prefixes<'cx, 'tcx> {
12231255
type Item = &'cx Lvalue<'tcx>;
12241256
fn next(&mut self) -> Option<Self::Item> {
12251257
let mut cursor = match self.next {
@@ -1246,8 +1278,6 @@ mod prefixes {
12461278
match proj.elem {
12471279
ProjectionElem::Field(_/*field*/, _/*ty*/) => {
12481280
// FIXME: add union handling
1249-
self.next = Some(&proj.base);
1250-
return Some(cursor);
12511281
}
12521282
ProjectionElem::Downcast(..) |
12531283
ProjectionElem::Subslice { .. } |
@@ -1256,58 +1286,10 @@ mod prefixes {
12561286
cursor = &proj.base;
12571287
continue 'cursor;
12581288
}
1259-
ProjectionElem::Deref => {
1260-
// (handled below)
1261-
}
1262-
}
1263-
1264-
assert_eq!(proj.elem, ProjectionElem::Deref);
1265-
1266-
match self.kind {
1267-
PrefixSet::Shallow => {
1268-
// shallow prefixes are found by stripping away
1269-
// fields, but stop at *any* dereference.
1270-
// So we can just stop the traversal now.
1271-
self.next = None;
1272-
return Some(cursor);
1273-
}
1274-
PrefixSet::All => {
1275-
// all prefixes: just blindly enqueue the base
1276-
// of the projection
1277-
self.next = Some(&proj.base);
1278-
return Some(cursor);
1279-
}
1280-
PrefixSet::Supporting => {
1281-
// fall through!
1282-
}
1283-
}
1284-
1285-
assert_eq!(self.kind, PrefixSet::Supporting);
1286-
// supporting prefixes: strip away fields and
1287-
// derefs, except we stop at the deref of a shared
1288-
// reference.
1289-
1290-
let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
1291-
match ty.sty {
1292-
ty::TyRawPtr(_) |
1293-
ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
1294-
// don't continue traversing over derefs of raw pointers or shared borrows.
1295-
self.next = None;
1296-
return Some(cursor);
1297-
}
1298-
1299-
ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
1300-
self.next = Some(&proj.base);
1301-
return Some(cursor);
1302-
}
1303-
1304-
ty::TyAdt(..) if ty.is_box() => {
1305-
self.next = Some(&proj.base);
1306-
return Some(cursor);
1307-
}
1308-
1309-
_ => panic!("unknown type fed to Projection Deref."),
1289+
_ => {}
13101290
}
1291+
self.next = Some(&proj.base);
1292+
return Some(cursor);
13111293
}
13121294
}
13131295
}

‎src/librustc_mir/dataflow/impls/borrows.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_data_structures::indexed_set::{IdxSet};
2020
use rustc_data_structures::indexed_vec::{IndexVec};
2121

2222
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
23+
use dataflow::abs_domain::{AbstractElem, Lift};
2324
pub use dataflow::indexes::BorrowIndex;
2425
use transform::nll::region_infer::RegionInferenceContext;
2526
use transform::nll::ToRegionVid;
@@ -44,13 +45,19 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
4445
// temporarily allow some dead fields: `kind` and `region` will be
4546
// needed by borrowck; `lvalue` will probably be a MovePathIndex when
4647
// that is extended to include borrowed data paths.
47-
#[allow(dead_code)]
4848
#[derive(Debug)]
4949
pub struct BorrowData<'tcx> {
5050
pub(crate) location: Location,
5151
pub(crate) kind: mir::BorrowKind,
5252
pub(crate) region: Region<'tcx>,
5353
pub(crate) lvalue: mir::Lvalue<'tcx>,
54+
55+
/// Projections in outermost-first order (e.g. `a[1].foo` => `[Field, Index]`).
56+
pub(crate) projections: Vec<AbstractElem<'tcx>>,
57+
/// The base lvalue of projections.
58+
pub(crate) base_lvalue: mir::Lvalue<'tcx>,
59+
/// Number of projections outside the outermost dereference.
60+
pub(crate) shallow_projections_len: usize,
5461
}
5562

5663
impl<'tcx> fmt::Display for BorrowData<'tcx> {
@@ -77,7 +84,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
7784
idx_vec: IndexVec::new(),
7885
location_map: FxHashMap(),
7986
region_map: FxHashMap(),
80-
region_span_map: FxHashMap()
87+
region_span_map: FxHashMap(),
8188
};
8289
visitor.visit_mir(mir);
8390
return Borrows { tcx: tcx,
@@ -96,16 +103,38 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
96103
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
97104
region_span_map: FxHashMap<RegionKind, Span>,
98105
}
99-
100-
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
106+
impl<'a, 'gcx: 'tcx, 'tcx: 'a> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
101107
fn visit_rvalue(&mut self,
102108
rvalue: &mir::Rvalue<'tcx>,
103109
location: mir::Location) {
104110
if let mir::Rvalue::Ref(region, kind, ref lvalue) = *rvalue {
105111
if is_unsafe_lvalue(self.tcx, self.mir, lvalue) { return; }
112+
let mut base_lvalue = lvalue;
113+
let mut projections = vec![];
114+
let mut shallow_projections_len = None;
115+
116+
while let mir::Lvalue::Projection(ref proj) = *base_lvalue {
117+
projections.push(proj.elem.lift());
118+
base_lvalue = &proj.base;
119+
120+
if let mir::ProjectionElem::Deref = proj.elem {
121+
if shallow_projections_len.is_none() {
122+
shallow_projections_len = Some(projections.len());
123+
}
124+
}
125+
}
126+
127+
let shallow_projections_len =
128+
shallow_projections_len.unwrap_or(projections.len());
106129

107130
let borrow = BorrowData {
108-
location: location, kind: kind, region: region, lvalue: lvalue.clone(),
131+
location,
132+
kind,
133+
region,
134+
lvalue: lvalue.clone(),
135+
projections,
136+
base_lvalue: base_lvalue.clone(),
137+
shallow_projections_len,
109138
};
110139
let idx = self.idx_vec.push(borrow);
111140
self.location_map.insert(location, idx);

‎src/librustc_mir/dataflow/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod drop_flag_effects;
3636
mod graphviz;
3737
mod impls;
3838
pub mod move_paths;
39+
pub mod abs_domain;
3940

4041
pub(crate) use self::move_paths::indexes;
4142

‎src/librustc_mir/dataflow/move_paths/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use syntax::codemap::DUMMY_SP;
1919
use std::collections::hash_map::Entry;
2020
use std::mem;
2121

22-
use super::abs_domain::Lift;
22+
use dataflow::abs_domain::Lift;
2323

2424
use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
2525
use super::{MoveError};

‎src/librustc_mir/dataflow/move_paths/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ use syntax_pos::{Span};
1818
use std::fmt;
1919
use std::ops::{Index, IndexMut};
2020

21-
use self::abs_domain::{AbstractElem, Lift};
22-
23-
mod abs_domain;
21+
use super::abs_domain::{AbstractElem, Lift};
2422

2523
// This submodule holds some newtype'd Index wrappers that are using
2624
// NonZero to ensure that Option<Index> occupies only a single word.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2017 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+
// revisions: ast mir
12+
//[mir]compile-flags: -Z borrowck-mir
13+
14+
fn use_x(_: usize) -> bool { true }
15+
16+
fn main() {
17+
}
18+
19+
fn foo() {
20+
let mut v = [1, 2, 3];
21+
let p = &v[0];
22+
if true {
23+
use_x(*p);
24+
} else {
25+
use_x(22);
26+
}
27+
v[0] += 1; //[ast]~ ERROR cannot assign to `v[..]` because it is borrowed
28+
//[mir]~^ cannot assign to `v[..]` because it is borrowed (Ast)
29+
//[mir]~| cannot assign to `v[..]` because it is borrowed (Mir)
30+
}
31+
32+
fn bar() {
33+
let mut v = [[1]];
34+
let p = &v[0][0];
35+
if true {
36+
use_x(*p);
37+
} else {
38+
use_x(22);
39+
}
40+
v[0][0] += 1; //[ast]~ ERROR cannot assign to `v[..][..]` because it is borrowed
41+
//[mir]~^ cannot assign to `v[..][..]` because it is borrowed (Ast)
42+
//[mir]~| cannot assign to `v[..][..]` because it is borrowed (Mir)
43+
}
44+
45+
fn baz() {
46+
struct S<T> { x: T, y: T, }
47+
let mut v = [S { x: 1, y: 2 },
48+
S { x: 3, y: 4 },
49+
S { x: 5, y: 6 }];
50+
let p = &v[0].x;
51+
if true {
52+
use_x(*p);
53+
} else {
54+
use_x(22);
55+
}
56+
v[0].x += 1; //[ast]~ ERROR cannot assign to `v[..].x` because it is borrowed
57+
//[mir]~^ cannot assign to `v[..].x` because it is borrowed (Ast)
58+
//[mir]~| cannot assign to `v[..].x` because it is borrowed (Mir)
59+
}

‎src/test/compile-fail/issue-25579.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ fn causes_ice(mut l: &mut Sexpression) {
3131
//[mir]~| ERROR (Mir) [E0499]
3232
}
3333
}}
34+
//[mir]~^ ERROR (Mir) [E0597]
35+
// FIXME
3436
}
3537

3638
fn main() {

0 commit comments

Comments
 (0)
Please sign in to comment.