@@ -64,7 +64,7 @@ use rustc_hir::def_id::DefId;
64
64
use rustc_hir:: lang_items:: { GeneratorStateLangItem , PinTypeLangItem } ;
65
65
use rustc_index:: bit_set:: { BitMatrix , BitSet } ;
66
66
use rustc_index:: vec:: { Idx , IndexVec } ;
67
- use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext } ;
67
+ use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
68
68
use rustc_middle:: mir:: * ;
69
69
use rustc_middle:: ty:: subst:: SubstsRef ;
70
70
use rustc_middle:: ty:: GeneratorSubsts ;
@@ -744,27 +744,19 @@ fn sanitize_witness<'tcx>(
744
744
}
745
745
746
746
fn compute_layout < ' tcx > (
747
- tcx : TyCtxt < ' tcx > ,
748
- source : MirSource < ' tcx > ,
749
- upvars : & Vec < Ty < ' tcx > > ,
750
- interior : Ty < ' tcx > ,
751
- always_live_locals : & storage:: AlwaysLiveLocals ,
752
- movable : bool ,
747
+ liveness : LivenessInfo ,
753
748
body : & mut Body < ' tcx > ,
754
749
) -> (
755
750
FxHashMap < Local , ( Ty < ' tcx > , VariantIdx , usize ) > ,
756
751
GeneratorLayout < ' tcx > ,
757
752
IndexVec < BasicBlock , Option < BitSet < Local > > > ,
758
753
) {
759
- // Use a liveness analysis to compute locals which are live across a suspension point
760
754
let LivenessInfo {
761
755
saved_locals,
762
756
live_locals_at_suspension_points,
763
757
storage_conflicts,
764
758
storage_liveness,
765
- } = locals_live_across_suspend_points ( tcx, body, source, always_live_locals, movable) ;
766
-
767
- sanitize_witness ( tcx, body, source. def_id ( ) , interior, upvars, & saved_locals) ;
759
+ } = liveness;
768
760
769
761
// Gather live local types and their indices.
770
762
let mut locals = IndexVec :: < GeneratorSavedLocal , _ > :: new ( ) ;
@@ -1279,11 +1271,25 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
1279
1271
1280
1272
let always_live_locals = storage:: AlwaysLiveLocals :: new ( & body) ;
1281
1273
1274
+ let liveness_info =
1275
+ locals_live_across_suspend_points ( tcx, body, source, & always_live_locals, movable) ;
1276
+
1277
+ sanitize_witness ( tcx, body, def_id, interior, & upvars, & liveness_info. saved_locals ) ;
1278
+
1279
+ if tcx. sess . opts . debugging_opts . validate_mir {
1280
+ let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
1281
+ assigned_local : None ,
1282
+ saved_locals : & liveness_info. saved_locals ,
1283
+ storage_conflicts : & liveness_info. storage_conflicts ,
1284
+ } ;
1285
+
1286
+ vis. visit_body ( body) ;
1287
+ }
1288
+
1282
1289
// Extract locals which are live across suspension point into `layout`
1283
1290
// `remap` gives a mapping from local indices onto generator struct indices
1284
1291
// `storage_liveness` tells us which locals have live storage at suspension points
1285
- let ( remap, layout, storage_liveness) =
1286
- compute_layout ( tcx, source, & upvars, interior, & always_live_locals, movable, body) ;
1292
+ let ( remap, layout, storage_liveness) = compute_layout ( liveness_info, body) ;
1287
1293
1288
1294
let can_return = can_return ( tcx, body) ;
1289
1295
@@ -1334,3 +1340,127 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
1334
1340
create_generator_resume_function ( tcx, transform, source, body, can_return) ;
1335
1341
}
1336
1342
}
1343
+
1344
+ /// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields
1345
+ /// in the generator state machine but whose storage does not conflict.
1346
+ ///
1347
+ /// This would occur if the assignment is the last use of `_5` but the initial definition of `_4`
1348
+ /// and we weren't extra careful to mark all locals used inside a statement as conflicting.
1349
+ /// Validation needs to happen immediately before `TransformVisitor` is invoked.
1350
+ ///
1351
+ /// This is could lead to miscompilations because the statement `_4 = _5` will be lowered to field
1352
+ /// accesses into the generator state machine
1353
+ struct EnsureGeneratorFieldAssignmentsNeverAlias < ' a > {
1354
+ saved_locals : & ' a GeneratorSavedLocals ,
1355
+ storage_conflicts : & ' a BitMatrix < GeneratorSavedLocal , GeneratorSavedLocal > ,
1356
+ assigned_local : Option < GeneratorSavedLocal > ,
1357
+ }
1358
+
1359
+ impl EnsureGeneratorFieldAssignmentsNeverAlias < ' _ > {
1360
+ fn saved_local_for_direct_place ( & self , place : Place < ' _ > ) -> Option < GeneratorSavedLocal > {
1361
+ if place. is_indirect ( ) {
1362
+ return None ;
1363
+ }
1364
+
1365
+ self . saved_locals . get ( place. local )
1366
+ }
1367
+
1368
+ fn check_assigned_place ( & mut self , place : Place < ' tcx > , f : impl FnOnce ( & mut Self ) ) {
1369
+ if let Some ( assigned_local) = self . saved_local_for_direct_place ( place) {
1370
+ assert ! ( self . assigned_local. is_none( ) , "`check_assigned_local` must not recurse" ) ;
1371
+
1372
+ self . assigned_local = Some ( assigned_local) ;
1373
+ f ( self ) ;
1374
+ self . assigned_local = None ;
1375
+ }
1376
+ }
1377
+ }
1378
+
1379
+ impl Visitor < ' tcx > for EnsureGeneratorFieldAssignmentsNeverAlias < ' _ > {
1380
+ fn visit_place ( & mut self , place : & Place < ' tcx > , context : PlaceContext , location : Location ) {
1381
+ let lhs = match self . assigned_local {
1382
+ Some ( l) => l,
1383
+ None => {
1384
+ // We should be visiting all places used in the MIR.
1385
+ assert ! ( !context. is_use( ) ) ;
1386
+ return ;
1387
+ }
1388
+ } ;
1389
+
1390
+ let rhs = match self . saved_local_for_direct_place ( * place) {
1391
+ Some ( l) => l,
1392
+ None => return ,
1393
+ } ;
1394
+
1395
+ if !self . storage_conflicts . contains ( lhs, rhs) {
1396
+ bug ! (
1397
+ "Assignment between generator saved locals whose storage does not conflict: \
1398
+ {:?}: {:?} = {:?}",
1399
+ location,
1400
+ lhs,
1401
+ rhs,
1402
+ ) ;
1403
+ }
1404
+ }
1405
+
1406
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
1407
+ match & statement. kind {
1408
+ StatementKind :: Assign ( box ( lhs, rhs) ) => {
1409
+ self . check_assigned_place ( * lhs, |this| this. visit_rvalue ( rhs, location) ) ;
1410
+ }
1411
+
1412
+ // FIXME: Does `llvm_asm!` have any aliasing requirements?
1413
+ StatementKind :: LlvmInlineAsm ( _) => { }
1414
+
1415
+ StatementKind :: FakeRead ( ..)
1416
+ | StatementKind :: SetDiscriminant { .. }
1417
+ | StatementKind :: StorageLive ( _)
1418
+ | StatementKind :: StorageDead ( _)
1419
+ | StatementKind :: Retag ( ..)
1420
+ | StatementKind :: AscribeUserType ( ..)
1421
+ | StatementKind :: Nop => { }
1422
+ }
1423
+ }
1424
+
1425
+ fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
1426
+ // Checking for aliasing in terminators is probably overkill, but until we have actual
1427
+ // semantics, we should be conservative here.
1428
+ match & terminator. kind {
1429
+ TerminatorKind :: Call {
1430
+ func,
1431
+ args,
1432
+ destination : Some ( ( dest, _) ) ,
1433
+ cleanup : _,
1434
+ from_hir_call : _,
1435
+ } => {
1436
+ self . check_assigned_place ( * dest, |this| {
1437
+ this. visit_operand ( func, location) ;
1438
+ for arg in args {
1439
+ this. visit_operand ( arg, location) ;
1440
+ }
1441
+ } ) ;
1442
+ }
1443
+
1444
+ TerminatorKind :: Yield { value, resume : _, resume_arg, drop : _ } => {
1445
+ self . check_assigned_place ( * resume_arg, |this| this. visit_operand ( value, location) ) ;
1446
+ }
1447
+
1448
+ // FIXME: Does `asm!` have any aliasing requirements?
1449
+ TerminatorKind :: InlineAsm { .. } => { }
1450
+
1451
+ TerminatorKind :: Call { .. }
1452
+ | TerminatorKind :: Goto { .. }
1453
+ | TerminatorKind :: SwitchInt { .. }
1454
+ | TerminatorKind :: Resume
1455
+ | TerminatorKind :: Abort
1456
+ | TerminatorKind :: Return
1457
+ | TerminatorKind :: Unreachable
1458
+ | TerminatorKind :: Drop { .. }
1459
+ | TerminatorKind :: DropAndReplace { .. }
1460
+ | TerminatorKind :: Assert { .. }
1461
+ | TerminatorKind :: GeneratorDrop
1462
+ | TerminatorKind :: FalseEdge { .. }
1463
+ | TerminatorKind :: FalseUnwind { .. } => { }
1464
+ }
1465
+ }
1466
+ }
0 commit comments