@@ -10,7 +10,7 @@ use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
1010use crate :: ty:: print:: { FmtPrinter , Printer } ;
1111use crate :: ty:: visit:: TypeVisitableExt ;
1212use crate :: ty:: { self , List , Ty , TyCtxt } ;
13- use crate :: ty:: { AdtDef , InstanceDef , UserTypeAnnotationIndex } ;
13+ use crate :: ty:: { AdtDef , Instance , InstanceDef , UserTypeAnnotationIndex } ;
1414use crate :: ty:: { GenericArg , GenericArgsRef } ;
1515
1616use rustc_data_structures:: captures:: Captures ;
@@ -27,6 +27,8 @@ pub use rustc_ast::Mutability;
2727use rustc_data_structures:: fx:: FxHashMap ;
2828use rustc_data_structures:: fx:: FxHashSet ;
2929use rustc_data_structures:: graph:: dominators:: Dominators ;
30+ use rustc_data_structures:: stack:: ensure_sufficient_stack;
31+ use rustc_index:: bit_set:: BitSet ;
3032use rustc_index:: { Idx , IndexSlice , IndexVec } ;
3133use rustc_serialize:: { Decodable , Encodable } ;
3234use rustc_span:: symbol:: Symbol ;
@@ -640,6 +642,129 @@ impl<'tcx> Body<'tcx> {
640642 self . injection_phase . is_some ( )
641643 }
642644
645+ /// Finds which basic blocks are actually reachable for a specific
646+ /// monomorphization of this body.
647+ ///
648+ /// This is allowed to have false positives; just because this says a block
649+ /// is reachable doesn't mean that's necessarily true. It's thus always
650+ /// legal for this to return a filled set.
651+ ///
652+ /// Regardless, the [`BitSet::domain_size`] of the returned set will always
653+ /// exactly match the number of blocks in the body so that `contains`
654+ /// checks can be done without worrying about panicking.
655+ ///
656+ /// This is mostly useful because it lets us skip lowering the `false` side
657+ /// of `if <T as Trait>::CONST`, as well as `intrinsics::debug_assertions`.
658+ pub fn reachable_blocks_in_mono (
659+ & self ,
660+ tcx : TyCtxt < ' tcx > ,
661+ instance : Instance < ' tcx > ,
662+ ) -> BitSet < BasicBlock > {
663+ let mut set = BitSet :: new_empty ( self . basic_blocks . len ( ) ) ;
664+ self . reachable_blocks_in_mono_from ( tcx, instance, & mut set, START_BLOCK ) ;
665+ set
666+ }
667+
668+ fn reachable_blocks_in_mono_from (
669+ & self ,
670+ tcx : TyCtxt < ' tcx > ,
671+ instance : Instance < ' tcx > ,
672+ set : & mut BitSet < BasicBlock > ,
673+ bb : BasicBlock ,
674+ ) {
675+ if !set. insert ( bb) {
676+ return ;
677+ }
678+
679+ let data = & self . basic_blocks [ bb] ;
680+
681+ if let Some ( ( bits, targets) ) = Self :: try_const_mono_switchint ( tcx, instance, data) {
682+ let target = targets. target_for_value ( bits) ;
683+ ensure_sufficient_stack ( || {
684+ self . reachable_blocks_in_mono_from ( tcx, instance, set, target)
685+ } ) ;
686+ return ;
687+ }
688+
689+ for target in data. terminator ( ) . successors ( ) {
690+ ensure_sufficient_stack ( || {
691+ self . reachable_blocks_in_mono_from ( tcx, instance, set, target)
692+ } ) ;
693+ }
694+ }
695+
696+ /// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the
697+ /// dimscriminant in monomorphization, we return the discriminant bits and the
698+ /// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator.
699+ fn try_const_mono_switchint < ' a > (
700+ tcx : TyCtxt < ' tcx > ,
701+ instance : Instance < ' tcx > ,
702+ block : & ' a BasicBlockData < ' tcx > ,
703+ ) -> Option < ( u128 , & ' a SwitchTargets ) > {
704+ // There are two places here we need to evaluate a constant.
705+ let eval_mono_const = |constant : & ConstOperand < ' tcx > | {
706+ let env = ty:: ParamEnv :: reveal_all ( ) ;
707+ let mono_literal = instance. instantiate_mir_and_normalize_erasing_regions (
708+ tcx,
709+ env,
710+ crate :: ty:: EarlyBinder :: bind ( constant. const_ ) ,
711+ ) ;
712+ let Some ( bits) = mono_literal. try_eval_bits ( tcx, env) else {
713+ bug ! ( "Couldn't evaluate constant {:?} in mono {:?}" , constant, instance) ;
714+ } ;
715+ bits
716+ } ;
717+
718+ let TerminatorKind :: SwitchInt { discr, targets } = & block. terminator ( ) . kind else {
719+ return None ;
720+ } ;
721+
722+ // If this is a SwitchInt(const _), then we can just evaluate the constant and return.
723+ let discr = match discr {
724+ Operand :: Constant ( constant) => {
725+ let bits = eval_mono_const ( constant) ;
726+ return Some ( ( bits, targets) ) ;
727+ }
728+ Operand :: Move ( place) | Operand :: Copy ( place) => place,
729+ } ;
730+
731+ // MIR for `if false` actually looks like this:
732+ // _1 = const _
733+ // SwitchInt(_1)
734+ //
735+ // And MIR for if intrinsics::debug_assertions() looks like this:
736+ // _1 = cfg!(debug_assertions)
737+ // SwitchInt(_1)
738+ //
739+ // So we're going to try to recognize this pattern.
740+ //
741+ // If we have a SwitchInt on a non-const place, we find the most recent statement that
742+ // isn't a storage marker. If that statement is an assignment of a const to our
743+ // discriminant place, we evaluate and return the const, as if we've const-propagated it
744+ // into the SwitchInt.
745+
746+ let last_stmt = block. statements . iter ( ) . rev ( ) . find ( |stmt| {
747+ !matches ! ( stmt. kind, StatementKind :: StorageDead ( _) | StatementKind :: StorageLive ( _) )
748+ } ) ?;
749+
750+ let ( place, rvalue) = last_stmt. kind . as_assign ( ) ?;
751+
752+ if discr != place {
753+ return None ;
754+ }
755+
756+ match rvalue {
757+ Rvalue :: NullaryOp ( NullOp :: UbCheck ( _) , _) => {
758+ Some ( ( tcx. sess . opts . debug_assertions as u128 , targets) )
759+ }
760+ Rvalue :: Use ( Operand :: Constant ( constant) ) => {
761+ let bits = eval_mono_const ( constant) ;
762+ Some ( ( bits, targets) )
763+ }
764+ _ => None ,
765+ }
766+ }
767+
643768 /// For a `Location` in this scope, determine what the "caller location" at that point is. This
644769 /// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions
645770 /// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions,
0 commit comments