@@ -6,7 +6,6 @@ use rustc_index::bit_set::BitSet;
66use rustc_index:: vec:: Idx ;
77use rustc_middle:: mir:: { self , Body , Location } ;
88use rustc_middle:: ty:: { self , TyCtxt } ;
9- use rustc_target:: abi:: VariantIdx ;
109
1110use super :: MoveDataParamEnv ;
1211
@@ -19,6 +18,7 @@ use super::drop_flag_effects_for_function_entry;
1918use super :: drop_flag_effects_for_location;
2019use super :: on_lookup_result_bits;
2120use crate :: dataflow:: drop_flag_effects;
21+ use crate :: dataflow:: framework:: SwitchIntEdgeEffects ;
2222
2323mod borrowed_locals;
2424pub ( super ) mod borrows;
@@ -352,24 +352,46 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
352352 ) ;
353353 }
354354
355- fn discriminant_switch_effect (
355+ fn switch_int_edge_effects < G : GenKill < Self :: Idx > > (
356356 & 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 > ,
362360 ) {
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+ } ) ;
373395 }
374396}
375397
@@ -441,28 +463,50 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
441463 ) ;
442464 }
443465
444- fn discriminant_switch_effect (
466+ fn switch_int_edge_effects < G : GenKill < Self :: Idx > > (
445467 & 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 > ,
451471 ) {
452472 if !self . mark_inactive_variants_as_uninit {
453473 return ;
454474 }
455475
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+ } ) ;
466510 }
467511}
468512
@@ -624,3 +668,42 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
624668 }
625669 }
626670}
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