Skip to content

Commit 0e35ddd

Browse files
committed
Auto merge of #49392 - retep007:nll-issue-48962, r=nikomatsakis
fixes move analysis Fixed compiler error by correct checking when dereferencing Fix #48962 r? @nikomatsakis
2 parents eeea94c + 9056c7a commit 0e35ddd

File tree

4 files changed

+167
-23
lines changed

4 files changed

+167
-23
lines changed

src/librustc_mir/borrow_check/error_reporting.rs

+17
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
3737
.collect::<Vec<_>>();
3838

3939
if mois.is_empty() {
40+
let root_place = self.prefixes(&place, PrefixSet::All)
41+
.last()
42+
.unwrap();
43+
44+
if self.moved_error_reported
45+
.contains(&root_place.clone())
46+
{
47+
debug!(
48+
"report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
49+
root_place
50+
);
51+
return;
52+
}
53+
54+
self.moved_error_reported
55+
.insert(root_place.clone());
56+
4057
let item_msg = match self.describe_place(place) {
4158
Some(name) => format!("`{}`", name),
4259
None => "value".to_owned(),

src/librustc_mir/borrow_check/mod.rs

+69-23
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
239239
},
240240
access_place_error_reported: FxHashSet(),
241241
reservation_error_reported: FxHashSet(),
242+
moved_error_reported: FxHashSet(),
242243
nonlexical_regioncx: opt_regioncx,
243244
nonlexical_cause_info: None,
244245
};
@@ -285,6 +286,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
285286
/// but it is currently inconvenient to track down the BorrowIndex
286287
/// at the time we detect and report a reservation error.
287288
reservation_error_reported: FxHashSet<Place<'tcx>>,
289+
/// This field keeps track of errors reported in the checking of moved variables,
290+
/// so that we don't report report seemingly duplicate errors.
291+
moved_error_reported: FxHashSet<Place<'tcx>>,
288292
/// Non-lexical region inference context, if NLL is enabled. This
289293
/// contains the results from region inference and lets us e.g.
290294
/// find out which CFG points are contained in each borrow region.
@@ -368,7 +372,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
368372
LocalMutationIsAllowed::No,
369373
flow_state,
370374
);
371-
self.check_if_path_is_moved(
375+
self.check_if_path_or_subpath_is_moved(
372376
context,
373377
InitializationRequiringAction::Use,
374378
(output, span),
@@ -965,7 +969,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
965969
// Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
966970
match mode {
967971
MutateMode::WriteAndRead => {
968-
self.check_if_path_is_moved(
972+
self.check_if_path_or_subpath_is_moved(
969973
context,
970974
InitializationRequiringAction::Update,
971975
place_span,
@@ -1025,7 +1029,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10251029
flow_state,
10261030
);
10271031

1028-
self.check_if_path_is_moved(
1032+
self.check_if_path_or_subpath_is_moved(
10291033
context,
10301034
InitializationRequiringAction::Borrow,
10311035
(place, span),
@@ -1053,7 +1057,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10531057
LocalMutationIsAllowed::No,
10541058
flow_state,
10551059
);
1056-
self.check_if_path_is_moved(
1060+
self.check_if_path_or_subpath_is_moved(
10571061
context,
10581062
InitializationRequiringAction::Use,
10591063
(place, span),
@@ -1100,7 +1104,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11001104
);
11011105

11021106
// Finally, check if path was already moved.
1103-
self.check_if_path_is_moved(
1107+
self.check_if_path_or_subpath_is_moved(
11041108
context,
11051109
InitializationRequiringAction::Use,
11061110
(place, span),
@@ -1118,7 +1122,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11181122
);
11191123

11201124
// Finally, check if path was already moved.
1121-
self.check_if_path_is_moved(
1125+
self.check_if_path_or_subpath_is_moved(
11221126
context,
11231127
InitializationRequiringAction::Use,
11241128
(place, span),
@@ -1269,7 +1273,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12691273
LocalMutationIsAllowed::No,
12701274
flow_state,
12711275
);
1272-
// We do not need to call `check_if_path_is_moved`
1276+
// We do not need to call `check_if_path_or_subpath_is_moved`
12731277
// again, as we already called it when we made the
12741278
// initial reservation.
12751279
}
@@ -1304,7 +1308,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13041308
}
13051309
}
13061310

1307-
fn check_if_path_is_moved(
1311+
fn check_if_full_path_is_moved(
13081312
&mut self,
13091313
context: Context,
13101314
desired_action: InitializationRequiringAction,
@@ -1322,18 +1326,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13221326
//
13231327
// 1. Move of `a.b.c`, use of `a.b.c`
13241328
// 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
1325-
// 3. Move of `a.b.c`, use of `a` or `a.b`
1326-
// 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
1329+
// 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
13271330
// partial initialization support, one might have `a.x`
13281331
// initialized but not `a.b`.
13291332
//
13301333
// OK scenarios:
13311334
//
1332-
// 5. Move of `a.b.c`, use of `a.b.d`
1333-
// 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1334-
// 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1335+
// 4. Move of `a.b.c`, use of `a.b.d`
1336+
// 5. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1337+
// 6. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
13351338
// must have been initialized for the use to be sound.
1336-
// 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1339+
// 7. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
13371340

13381341
// The dataflow tracks shallow prefixes distinctly (that is,
13391342
// field-accesses on P distinctly from P itself), in order to
@@ -1352,9 +1355,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13521355
// have a MovePath, that should capture the initialization
13531356
// state for the place scenario.
13541357
//
1355-
// This code covers scenarios 1, 2, and 4.
1358+
// This code covers scenarios 1, 2, and 3.
13561359

1357-
debug!("check_if_path_is_moved part1 place: {:?}", place);
1360+
debug!("check_if_full_path_is_moved place: {:?}", place);
13581361
match self.move_path_closest_to(place) {
13591362
Ok(mpi) => {
13601363
if maybe_uninits.contains(&mpi) {
@@ -1374,18 +1377,52 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13741377
// ancestors; dataflow recurs on children when parents
13751378
// move (to support partial (re)inits).
13761379
//
1377-
// (I.e. querying parents breaks scenario 8; but may want
1380+
// (I.e. querying parents breaks scenario 7; but may want
13781381
// to do such a query based on partial-init feature-gate.)
13791382
}
1383+
}
1384+
1385+
fn check_if_path_or_subpath_is_moved(
1386+
&mut self,
1387+
context: Context,
1388+
desired_action: InitializationRequiringAction,
1389+
place_span: (&Place<'tcx>, Span),
1390+
flow_state: &Flows<'cx, 'gcx, 'tcx>,
1391+
) {
1392+
// FIXME: analogous code in check_loans first maps `place` to
1393+
// its base_path ... but is that what we want here?
1394+
let place = self.base_path(place_span.0);
1395+
1396+
let maybe_uninits = &flow_state.uninits;
1397+
let curr_move_outs = &flow_state.move_outs;
1398+
1399+
// Bad scenarios:
1400+
//
1401+
// 1. Move of `a.b.c`, use of `a` or `a.b`
1402+
// partial initialization support, one might have `a.x`
1403+
// initialized but not `a.b`.
1404+
// 2. All bad scenarios from `check_if_full_path_is_moved`
1405+
//
1406+
// OK scenarios:
1407+
//
1408+
// 3. Move of `a.b.c`, use of `a.b.d`
1409+
// 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1410+
// 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1411+
// must have been initialized for the use to be sound.
1412+
// 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1413+
1414+
self.check_if_full_path_is_moved(context, desired_action, place_span, flow_state);
13801415

13811416
// A move of any shallow suffix of `place` also interferes
13821417
// with an attempt to use `place`. This is scenario 3 above.
13831418
//
13841419
// (Distinct from handling of scenarios 1+2+4 above because
13851420
// `place` does not interfere with suffixes of its prefixes,
13861421
// e.g. `a.b.c` does not interfere with `a.b.d`)
1422+
//
1423+
// This code covers scenario 1.
13871424

1388-
debug!("check_if_path_is_moved part2 place: {:?}", place);
1425+
debug!("check_if_path_or_subpath_is_moved place: {:?}", place);
13891426
if let Some(mpi) = self.move_path_for_place(place) {
13901427
if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) {
13911428
self.report_use_of_moved_or_uninitialized(
@@ -1445,7 +1482,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
14451482
(place, span): (&Place<'tcx>, Span),
14461483
flow_state: &Flows<'cx, 'gcx, 'tcx>,
14471484
) {
1448-
// recur down place; dispatch to check_if_path_is_moved when necessary
1485+
debug!("check_if_assigned_path_is_moved place: {:?}", place);
1486+
// recur down place; dispatch to external checks when necessary
14491487
let mut place = place;
14501488
loop {
14511489
match *place {
@@ -1456,17 +1494,25 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
14561494
Place::Projection(ref proj) => {
14571495
let Projection { ref base, ref elem } = **proj;
14581496
match *elem {
1459-
ProjectionElem::Deref |
1460-
// assigning to *P requires `P` initialized.
14611497
ProjectionElem::Index(_/*operand*/) |
14621498
ProjectionElem::ConstantIndex { .. } |
1463-
// assigning to P[i] requires `P` initialized.
1499+
// assigning to P[i] requires P to be valid.
14641500
ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
14651501
// assigning to (P->variant) is okay if assigning to `P` is okay
14661502
//
14671503
// FIXME: is this true even if P is a adt with a dtor?
14681504
{ }
14691505

1506+
// assigning to (*P) requires P to be initialized
1507+
ProjectionElem::Deref => {
1508+
self.check_if_full_path_is_moved(
1509+
context, InitializationRequiringAction::Use,
1510+
(base, span), flow_state);
1511+
// (base initialized; no need to
1512+
// recur further)
1513+
break;
1514+
}
1515+
14701516
ProjectionElem::Subslice { .. } => {
14711517
panic!("we don't allow assignments to subslices, context: {:?}",
14721518
context);
@@ -1484,7 +1530,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
14841530
// check_loans.rs first maps
14851531
// `base` to its base_path.
14861532

1487-
self.check_if_path_is_moved(
1533+
self.check_if_path_or_subpath_is_moved(
14881534
context, InitializationRequiringAction::Assignment,
14891535
(base, span), flow_state);
14901536

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2018 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(nll)]
12+
13+
struct Node {
14+
elem: i32,
15+
next: Option<Box<Node>>,
16+
}
17+
18+
fn a() {
19+
let mut node = Node {
20+
elem: 5,
21+
next: None,
22+
};
23+
24+
let mut src = &mut node;
25+
{src};
26+
src.next = None; //~ ERROR use of moved value: `src` [E0382]
27+
}
28+
29+
fn b() {
30+
let mut src = &mut (22, 44);
31+
{src};
32+
src.0 = 66; //~ ERROR use of moved value: `src` [E0382]
33+
}
34+
35+
fn main() {
36+
a();
37+
b();
38+
}

src/test/run-pass/issue-48962.rs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2018 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+
// Test that we are able to reinitilize box with moved referent
12+
#![feature(nll)]
13+
static mut ORDER: [usize; 3] = [0, 0, 0];
14+
static mut INDEX: usize = 0;
15+
16+
struct Dropee (usize);
17+
18+
impl Drop for Dropee {
19+
fn drop(&mut self) {
20+
unsafe {
21+
ORDER[INDEX] = self.0;
22+
INDEX = INDEX + 1;
23+
}
24+
}
25+
}
26+
27+
fn add_sentintel() {
28+
unsafe {
29+
ORDER[INDEX] = 2;
30+
INDEX = INDEX + 1;
31+
}
32+
}
33+
34+
fn main() {
35+
let mut x = Box::new(Dropee(1));
36+
*x; // move out from `*x`
37+
add_sentintel();
38+
*x = Dropee(3); // re-initialize `*x`
39+
{x}; // drop value
40+
unsafe {
41+
assert_eq!(ORDER, [1, 2, 3]);
42+
}
43+
}

0 commit comments

Comments
 (0)