@@ -6,7 +6,6 @@ use rustc_index::bit_set::BitSet;
6
6
use rustc_index:: vec:: Idx ;
7
7
use rustc_middle:: mir:: { self , Body , Location } ;
8
8
use rustc_middle:: ty:: { self , TyCtxt } ;
9
- use rustc_target:: abi:: VariantIdx ;
10
9
11
10
use super :: MoveDataParamEnv ;
12
11
@@ -19,6 +18,7 @@ use super::drop_flag_effects_for_function_entry;
19
18
use super :: drop_flag_effects_for_location;
20
19
use super :: on_lookup_result_bits;
21
20
use crate :: dataflow:: drop_flag_effects;
21
+ use crate :: dataflow:: framework:: SwitchIntEdgeEffects ;
22
22
23
23
mod borrowed_locals;
24
24
pub ( super ) mod borrows;
@@ -352,24 +352,46 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
352
352
) ;
353
353
}
354
354
355
- fn discriminant_switch_effect (
355
+ fn switch_int_edge_effects < G : GenKill < Self :: Idx > > (
356
356
& self ,
357
- trans : & mut impl GenKill < Self :: Idx > ,
358
- _block : mir:: BasicBlock ,
359
- enum_place : mir:: Place < ' tcx > ,
360
- _adt : & ty:: AdtDef ,
361
- variant : VariantIdx ,
357
+ block : mir:: BasicBlock ,
358
+ discr : & mir:: Operand < ' tcx > ,
359
+ edge_effects : & mut impl SwitchIntEdgeEffects < G > ,
362
360
) {
363
- // Kill all move paths that correspond to variants we know to be inactive along this
364
- // particular outgoing edge of a `SwitchInt`.
365
- drop_flag_effects:: on_all_inactive_variants (
366
- self . tcx ,
367
- self . body ,
368
- self . move_data ( ) ,
369
- enum_place,
370
- variant,
371
- |mpi| trans. kill ( mpi) ,
372
- ) ;
361
+ let enum_ = discr. place ( ) . and_then ( |discr| {
362
+ switch_on_enum_discriminant ( self . tcx , & self . body , & self . body [ block] , discr)
363
+ } ) ;
364
+
365
+ let ( enum_place, enum_def) = match enum_ {
366
+ Some ( x) => x,
367
+ None => return ,
368
+ } ;
369
+
370
+ let mut discriminants = enum_def. discriminants ( self . tcx ) ;
371
+ edge_effects. apply ( |trans, edge| {
372
+ let value = match edge. value {
373
+ Some ( x) => x,
374
+ None => return ,
375
+ } ;
376
+
377
+ // MIR building adds discriminants to the `values` array in the same order as they
378
+ // are yielded by `AdtDef::discriminants`. We rely on this to match each
379
+ // discriminant in `values` to its corresponding variant in linear time.
380
+ let ( variant, _) = discriminants
381
+ . find ( |& ( _, discr) | discr. val == value)
382
+ . expect ( "Order of `AdtDef::discriminants` differed from `SwitchInt::values`" ) ;
383
+
384
+ // Kill all move paths that correspond to variants we know to be inactive along this
385
+ // particular outgoing edge of a `SwitchInt`.
386
+ drop_flag_effects:: on_all_inactive_variants (
387
+ self . tcx ,
388
+ self . body ,
389
+ self . move_data ( ) ,
390
+ enum_place,
391
+ variant,
392
+ |mpi| trans. kill ( mpi) ,
393
+ ) ;
394
+ } ) ;
373
395
}
374
396
}
375
397
@@ -441,28 +463,50 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
441
463
) ;
442
464
}
443
465
444
- fn discriminant_switch_effect (
466
+ fn switch_int_edge_effects < G : GenKill < Self :: Idx > > (
445
467
& self ,
446
- trans : & mut impl GenKill < Self :: Idx > ,
447
- _block : mir:: BasicBlock ,
448
- enum_place : mir:: Place < ' tcx > ,
449
- _adt : & ty:: AdtDef ,
450
- variant : VariantIdx ,
468
+ block : mir:: BasicBlock ,
469
+ discr : & mir:: Operand < ' tcx > ,
470
+ edge_effects : & mut impl SwitchIntEdgeEffects < G > ,
451
471
) {
452
472
if !self . mark_inactive_variants_as_uninit {
453
473
return ;
454
474
}
455
475
456
- // Mark all move paths that correspond to variants other than this one as maybe
457
- // uninitialized (in reality, they are *definitely* uninitialized).
458
- drop_flag_effects:: on_all_inactive_variants (
459
- self . tcx ,
460
- self . body ,
461
- self . move_data ( ) ,
462
- enum_place,
463
- variant,
464
- |mpi| trans. gen ( mpi) ,
465
- ) ;
476
+ let enum_ = discr. place ( ) . and_then ( |discr| {
477
+ switch_on_enum_discriminant ( self . tcx , & self . body , & self . body [ block] , discr)
478
+ } ) ;
479
+
480
+ let ( enum_place, enum_def) = match enum_ {
481
+ Some ( x) => x,
482
+ None => return ,
483
+ } ;
484
+
485
+ let mut discriminants = enum_def. discriminants ( self . tcx ) ;
486
+ edge_effects. apply ( |trans, edge| {
487
+ let value = match edge. value {
488
+ Some ( x) => x,
489
+ None => return ,
490
+ } ;
491
+
492
+ // MIR building adds discriminants to the `values` array in the same order as they
493
+ // are yielded by `AdtDef::discriminants`. We rely on this to match each
494
+ // discriminant in `values` to its corresponding variant in linear time.
495
+ let ( variant, _) = discriminants
496
+ . find ( |& ( _, discr) | discr. val == value)
497
+ . expect ( "Order of `AdtDef::discriminants` differed from `SwitchInt::values`" ) ;
498
+
499
+ // Mark all move paths that correspond to variants other than this one as maybe
500
+ // uninitialized (in reality, they are *definitely* uninitialized).
501
+ drop_flag_effects:: on_all_inactive_variants (
502
+ self . tcx ,
503
+ self . body ,
504
+ self . move_data ( ) ,
505
+ enum_place,
506
+ variant,
507
+ |mpi| trans. gen ( mpi) ,
508
+ ) ;
509
+ } ) ;
466
510
}
467
511
}
468
512
@@ -624,3 +668,42 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
624
668
}
625
669
}
626
670
}
671
+
672
+ /// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is
673
+ /// an enum discriminant.
674
+ ///
675
+ /// We expect such blocks to have a call to `discriminant` as their last statement like so:
676
+ ///
677
+ /// ```text
678
+ /// ...
679
+ /// _42 = discriminant(_1)
680
+ /// SwitchInt(_42, ..)
681
+ /// ```
682
+ ///
683
+ /// If the basic block matches this pattern, this function returns the place corresponding to the
684
+ /// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
685
+ fn switch_on_enum_discriminant (
686
+ tcx : TyCtxt < ' tcx > ,
687
+ body : & ' mir mir:: Body < ' tcx > ,
688
+ block : & ' mir mir:: BasicBlockData < ' tcx > ,
689
+ switch_on : mir:: Place < ' tcx > ,
690
+ ) -> Option < ( mir:: Place < ' tcx > , & ' tcx ty:: AdtDef ) > {
691
+ match block. statements . last ( ) . map ( |stmt| & stmt. kind ) {
692
+ Some ( mir:: StatementKind :: Assign ( box ( lhs, mir:: Rvalue :: Discriminant ( discriminated) ) ) )
693
+ if * lhs == switch_on =>
694
+ {
695
+ match & discriminated. ty ( body, tcx) . ty . kind ( ) {
696
+ ty:: Adt ( def, _) => Some ( ( * discriminated, def) ) ,
697
+
698
+ // `Rvalue::Discriminant` is also used to get the active yield point for a
699
+ // generator, but we do not need edge-specific effects in that case. This may
700
+ // change in the future.
701
+ ty:: Generator ( ..) => None ,
702
+
703
+ t => bug ! ( "`discriminant` called on unexpected type {:?}" , t) ,
704
+ }
705
+ }
706
+
707
+ _ => None ,
708
+ }
709
+ }
0 commit comments