@@ -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 ( ) ;
@@ -1280,11 +1272,25 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
1280
1272
1281
1273
let always_live_locals = storage:: AlwaysLiveLocals :: new ( & body) ;
1282
1274
1275
+ let liveness_info =
1276
+ locals_live_across_suspend_points ( tcx, body, source, & always_live_locals, movable) ;
1277
+
1278
+ sanitize_witness ( tcx, body, def_id, interior, & upvars, & liveness_info. saved_locals ) ;
1279
+
1280
+ if tcx. sess . opts . debugging_opts . validate_mir {
1281
+ let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
1282
+ assigned_local : None ,
1283
+ saved_locals : & liveness_info. saved_locals ,
1284
+ storage_conflicts : & liveness_info. storage_conflicts ,
1285
+ } ;
1286
+
1287
+ vis. visit_body ( body) ;
1288
+ }
1289
+
1283
1290
// Extract locals which are live across suspension point into `layout`
1284
1291
// `remap` gives a mapping from local indices onto generator struct indices
1285
1292
// `storage_liveness` tells us which locals have live storage at suspension points
1286
- let ( remap, layout, storage_liveness) =
1287
- compute_layout ( tcx, source, & upvars, interior, & always_live_locals, movable, body) ;
1293
+ let ( remap, layout, storage_liveness) = compute_layout ( liveness_info, body) ;
1288
1294
1289
1295
let can_return = can_return ( tcx, body) ;
1290
1296
@@ -1335,3 +1341,131 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
1335
1341
create_generator_resume_function ( tcx, transform, source, body, can_return) ;
1336
1342
}
1337
1343
}
1344
+
1345
+ /// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields
1346
+ /// in the generator state machine but whose storage does not conflict.
1347
+ ///
1348
+ /// Validation needs to happen immediately *before* `TransformVisitor` is invoked, not after.
1349
+ ///
1350
+ /// This condition would arise when the assignment is the last use of `_5` but the initial
1351
+ /// definition of `_4` if we weren't extra careful to mark all locals used inside a statement as
1352
+ /// conflicting. Non-conflicting generator saved locals may be stored at the same location within
1353
+ /// the generator state machine, which would result in ill-formed MIR: the left-hand and right-hand
1354
+ /// sides of an assignment may not alias. This caused a miscompilation in [#73137].
1355
+ ///
1356
+ /// [#73137]: https://github.com/rust-lang/rust/issues/73137
1357
+ struct EnsureGeneratorFieldAssignmentsNeverAlias < ' a > {
1358
+ saved_locals : & ' a GeneratorSavedLocals ,
1359
+ storage_conflicts : & ' a BitMatrix < GeneratorSavedLocal , GeneratorSavedLocal > ,
1360
+ assigned_local : Option < GeneratorSavedLocal > ,
1361
+ }
1362
+
1363
+ impl EnsureGeneratorFieldAssignmentsNeverAlias < ' _ > {
1364
+ fn saved_local_for_direct_place ( & self , place : Place < ' _ > ) -> Option < GeneratorSavedLocal > {
1365
+ if place. is_indirect ( ) {
1366
+ return None ;
1367
+ }
1368
+
1369
+ self . saved_locals . get ( place. local )
1370
+ }
1371
+
1372
+ fn check_assigned_place ( & mut self , place : Place < ' tcx > , f : impl FnOnce ( & mut Self ) ) {
1373
+ if let Some ( assigned_local) = self . saved_local_for_direct_place ( place) {
1374
+ assert ! ( self . assigned_local. is_none( ) , "`check_assigned_local` must not recurse" ) ;
1375
+
1376
+ self . assigned_local = Some ( assigned_local) ;
1377
+ f ( self ) ;
1378
+ self . assigned_local = None ;
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ impl Visitor < ' tcx > for EnsureGeneratorFieldAssignmentsNeverAlias < ' _ > {
1384
+ fn visit_place ( & mut self , place : & Place < ' tcx > , context : PlaceContext , location : Location ) {
1385
+ let lhs = match self . assigned_local {
1386
+ Some ( l) => l,
1387
+ None => {
1388
+ // We should be visiting all places used in the MIR.
1389
+ assert ! ( !context. is_use( ) ) ;
1390
+ return ;
1391
+ }
1392
+ } ;
1393
+
1394
+ let rhs = match self . saved_local_for_direct_place ( * place) {
1395
+ Some ( l) => l,
1396
+ None => return ,
1397
+ } ;
1398
+
1399
+ if !self . storage_conflicts . contains ( lhs, rhs) {
1400
+ bug ! (
1401
+ "Assignment between generator saved locals whose storage does not conflict: \
1402
+ {:?}: {:?} = {:?}",
1403
+ location,
1404
+ lhs,
1405
+ rhs,
1406
+ ) ;
1407
+ }
1408
+ }
1409
+
1410
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
1411
+ match & statement. kind {
1412
+ StatementKind :: Assign ( box ( lhs, rhs) ) => {
1413
+ self . check_assigned_place ( * lhs, |this| this. visit_rvalue ( rhs, location) ) ;
1414
+ }
1415
+
1416
+ // FIXME: Does `llvm_asm!` have any aliasing requirements?
1417
+ StatementKind :: LlvmInlineAsm ( _) => { }
1418
+
1419
+ StatementKind :: FakeRead ( ..)
1420
+ | StatementKind :: SetDiscriminant { .. }
1421
+ | StatementKind :: StorageLive ( _)
1422
+ | StatementKind :: StorageDead ( _)
1423
+ | StatementKind :: Retag ( ..)
1424
+ | StatementKind :: AscribeUserType ( ..)
1425
+ | StatementKind :: Nop => { }
1426
+ }
1427
+ }
1428
+
1429
+ fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
1430
+ // Checking for aliasing in terminators is probably overkill, but until we have actual
1431
+ // semantics, we should be conservative here.
1432
+ match & terminator. kind {
1433
+ TerminatorKind :: Call {
1434
+ func,
1435
+ args,
1436
+ destination : Some ( ( dest, _) ) ,
1437
+ cleanup : _,
1438
+ from_hir_call : _,
1439
+ fn_span : _,
1440
+ } => {
1441
+ self . check_assigned_place ( * dest, |this| {
1442
+ this. visit_operand ( func, location) ;
1443
+ for arg in args {
1444
+ this. visit_operand ( arg, location) ;
1445
+ }
1446
+ } ) ;
1447
+ }
1448
+
1449
+ TerminatorKind :: Yield { value, resume : _, resume_arg, drop : _ } => {
1450
+ self . check_assigned_place ( * resume_arg, |this| this. visit_operand ( value, location) ) ;
1451
+ }
1452
+
1453
+ // FIXME: Does `asm!` have any aliasing requirements?
1454
+ TerminatorKind :: InlineAsm { .. } => { }
1455
+
1456
+ TerminatorKind :: Call { .. }
1457
+ | TerminatorKind :: Goto { .. }
1458
+ | TerminatorKind :: SwitchInt { .. }
1459
+ | TerminatorKind :: Resume
1460
+ | TerminatorKind :: Abort
1461
+ | TerminatorKind :: Return
1462
+ | TerminatorKind :: Unreachable
1463
+ | TerminatorKind :: Drop { .. }
1464
+ | TerminatorKind :: DropAndReplace { .. }
1465
+ | TerminatorKind :: Assert { .. }
1466
+ | TerminatorKind :: GeneratorDrop
1467
+ | TerminatorKind :: FalseEdge { .. }
1468
+ | TerminatorKind :: FalseUnwind { .. } => { }
1469
+ }
1470
+ }
1471
+ }
0 commit comments