@@ -239,6 +239,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
239
239
} ,
240
240
access_place_error_reported : FxHashSet ( ) ,
241
241
reservation_error_reported : FxHashSet ( ) ,
242
+ moved_error_reported : FxHashSet ( ) ,
242
243
nonlexical_regioncx : opt_regioncx,
243
244
nonlexical_cause_info : None ,
244
245
} ;
@@ -285,6 +286,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
285
286
/// but it is currently inconvenient to track down the BorrowIndex
286
287
/// at the time we detect and report a reservation error.
287
288
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 > > ,
288
292
/// Non-lexical region inference context, if NLL is enabled. This
289
293
/// contains the results from region inference and lets us e.g.
290
294
/// 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
368
372
LocalMutationIsAllowed :: No ,
369
373
flow_state,
370
374
) ;
371
- self . check_if_path_is_moved (
375
+ self . check_if_path_or_subpath_is_moved (
372
376
context,
373
377
InitializationRequiringAction :: Use ,
374
378
( output, span) ,
@@ -963,7 +967,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
963
967
// Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
964
968
match mode {
965
969
MutateMode :: WriteAndRead => {
966
- self . check_if_path_is_moved (
970
+ self . check_if_path_or_subpath_is_moved (
967
971
context,
968
972
InitializationRequiringAction :: Update ,
969
973
place_span,
@@ -1023,7 +1027,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1023
1027
flow_state,
1024
1028
) ;
1025
1029
1026
- self . check_if_path_is_moved (
1030
+ self . check_if_path_or_subpath_is_moved (
1027
1031
context,
1028
1032
InitializationRequiringAction :: Borrow ,
1029
1033
( place, span) ,
@@ -1051,7 +1055,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1051
1055
LocalMutationIsAllowed :: No ,
1052
1056
flow_state,
1053
1057
) ;
1054
- self . check_if_path_is_moved (
1058
+ self . check_if_path_or_subpath_is_moved (
1055
1059
context,
1056
1060
InitializationRequiringAction :: Use ,
1057
1061
( place, span) ,
@@ -1098,7 +1102,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1098
1102
) ;
1099
1103
1100
1104
// Finally, check if path was already moved.
1101
- self . check_if_path_is_moved (
1105
+ self . check_if_path_or_subpath_is_moved (
1102
1106
context,
1103
1107
InitializationRequiringAction :: Use ,
1104
1108
( place, span) ,
@@ -1116,7 +1120,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1116
1120
) ;
1117
1121
1118
1122
// Finally, check if path was already moved.
1119
- self . check_if_path_is_moved (
1123
+ self . check_if_path_or_subpath_is_moved (
1120
1124
context,
1121
1125
InitializationRequiringAction :: Use ,
1122
1126
( place, span) ,
@@ -1267,7 +1271,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1267
1271
LocalMutationIsAllowed :: No ,
1268
1272
flow_state,
1269
1273
) ;
1270
- // We do not need to call `check_if_path_is_moved `
1274
+ // We do not need to call `check_if_path_or_subpath_is_moved `
1271
1275
// again, as we already called it when we made the
1272
1276
// initial reservation.
1273
1277
}
@@ -1320,18 +1324,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1320
1324
//
1321
1325
// 1. Move of `a.b.c`, use of `a.b.c`
1322
1326
// 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
1323
- // 3. Move of `a.b.c`, use of `a` or `a.b`
1324
- // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
1327
+ // 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
1325
1328
// partial initialization support, one might have `a.x`
1326
1329
// initialized but not `a.b`.
1327
1330
//
1328
1331
// OK scenarios:
1329
1332
//
1330
- // 5 . Move of `a.b.c`, use of `a.b.d`
1331
- // 6 . Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1332
- // 7 . Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1333
+ // 4 . Move of `a.b.c`, use of `a.b.d`
1334
+ // 5 . Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1335
+ // 6 . Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1333
1336
// must have been initialized for the use to be sound.
1334
- // 8 . Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1337
+ // 7 . Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1335
1338
1336
1339
// The dataflow tracks shallow prefixes distinctly (that is,
1337
1340
// field-accesses on P distinctly from P itself), in order to
@@ -1350,9 +1353,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1350
1353
// have a MovePath, that should capture the initialization
1351
1354
// state for the place scenario.
1352
1355
//
1353
- // This code covers scenarios 1, 2, and 4 .
1356
+ // This code covers scenarios 1, 2, and 3 .
1354
1357
1355
- debug ! ( "check_if_path_is_moved part1 place: {:?}" , place) ;
1358
+ debug ! ( "check_if_path_is_moved place: {:?}" , place) ;
1356
1359
match self . move_path_closest_to ( place) {
1357
1360
Ok ( mpi) => {
1358
1361
if maybe_uninits. contains ( & mpi) {
@@ -1372,18 +1375,52 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1372
1375
// ancestors; dataflow recurs on children when parents
1373
1376
// move (to support partial (re)inits).
1374
1377
//
1375
- // (I.e. querying parents breaks scenario 8 ; but may want
1378
+ // (I.e. querying parents breaks scenario 7 ; but may want
1376
1379
// to do such a query based on partial-init feature-gate.)
1377
1380
}
1381
+ }
1382
+
1383
+ fn check_if_path_or_subpath_is_moved (
1384
+ & mut self ,
1385
+ context : Context ,
1386
+ desired_action : InitializationRequiringAction ,
1387
+ place_span : ( & Place < ' tcx > , Span ) ,
1388
+ flow_state : & Flows < ' cx , ' gcx , ' tcx > ,
1389
+ ) {
1390
+ // FIXME: analogous code in check_loans first maps `place` to
1391
+ // its base_path ... but is that what we want here?
1392
+ let place = self . base_path ( place_span. 0 ) ;
1393
+
1394
+ let maybe_uninits = & flow_state. uninits ;
1395
+ let curr_move_outs = & flow_state. move_outs ;
1396
+
1397
+ // Bad scenarios:
1398
+ //
1399
+ // 1. Move of `a.b.c`, use of `a` or `a.b`
1400
+ // partial initialization support, one might have `a.x`
1401
+ // initialized but not `a.b`.
1402
+ // 2. All bad scenarios from `check_if_path_is_moved`
1403
+ //
1404
+ // OK scenarios:
1405
+ //
1406
+ // 3. Move of `a.b.c`, use of `a.b.d`
1407
+ // 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
1408
+ // 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
1409
+ // must have been initialized for the use to be sound.
1410
+ // 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
1411
+
1412
+ self . check_if_path_is_moved ( context, desired_action, place_span, flow_state) ;
1378
1413
1379
1414
// A move of any shallow suffix of `place` also interferes
1380
1415
// with an attempt to use `place`. This is scenario 3 above.
1381
1416
//
1382
1417
// (Distinct from handling of scenarios 1+2+4 above because
1383
1418
// `place` does not interfere with suffixes of its prefixes,
1384
1419
// e.g. `a.b.c` does not interfere with `a.b.d`)
1420
+ //
1421
+ // This code covers scenario 1.
1385
1422
1386
- debug ! ( "check_if_path_is_moved part2 place: {:?}" , place) ;
1423
+ debug ! ( "check_if_path_or_subpath_is_moved place: {:?}" , place) ;
1387
1424
if let Some ( mpi) = self . move_path_for_place ( place) {
1388
1425
if let Some ( child_mpi) = maybe_uninits. has_any_child_of ( mpi) {
1389
1426
self . report_use_of_moved_or_uninitialized (
@@ -1443,7 +1480,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1443
1480
( place, span) : ( & Place < ' tcx > , Span ) ,
1444
1481
flow_state : & Flows < ' cx , ' gcx , ' tcx > ,
1445
1482
) {
1446
- // recur down place; dispatch to check_if_path_is_moved when necessary
1483
+ debug ! ( "check_if_assigned_path_is_moved place: {:?}" , place) ;
1484
+ // recur down place; dispatch to external checks when necessary
1447
1485
let mut place = place;
1448
1486
loop {
1449
1487
match * place {
@@ -1454,8 +1492,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1454
1492
Place :: Projection ( ref proj) => {
1455
1493
let Projection { ref base, ref elem } = * * proj;
1456
1494
match * elem {
1457
- ProjectionElem :: Deref |
1458
- // assigning to *P requires `P` initialized.
1459
1495
ProjectionElem :: Index ( _/*operand*/ ) |
1460
1496
ProjectionElem :: ConstantIndex { .. } |
1461
1497
// assigning to P[i] requires `P` initialized.
@@ -1465,6 +1501,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1465
1501
// FIXME: is this true even if P is a adt with a dtor?
1466
1502
{ }
1467
1503
1504
+ ProjectionElem :: Deref => {
1505
+ self . check_if_path_is_moved (
1506
+ context, InitializationRequiringAction :: Use ,
1507
+ ( base, span) , flow_state) ;
1508
+ }
1509
+
1468
1510
ProjectionElem :: Subslice { .. } => {
1469
1511
panic ! ( "we don't allow assignments to subslices, context: {:?}" ,
1470
1512
context) ;
@@ -1482,7 +1524,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1482
1524
// check_loans.rs first maps
1483
1525
// `base` to its base_path.
1484
1526
1485
- self . check_if_path_is_moved (
1527
+ self . check_if_path_or_subpath_is_moved (
1486
1528
context, InitializationRequiringAction :: Assignment ,
1487
1529
( base, span) , flow_state) ;
1488
1530
0 commit comments