@@ -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
0 commit comments