@@ -480,6 +480,76 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
480480 Some ( ( ) )
481481 }
482482
483+ fn check_assertion (
484+ & mut self ,
485+ expected : bool ,
486+ msg : & AssertKind < Operand < ' tcx > > ,
487+ cond : & Operand < ' tcx > ,
488+ location : Location ,
489+ ) -> Option < !> {
490+ let ref value = self . eval_operand ( & cond, location) ?;
491+ trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
492+
493+ let expected = Scalar :: from_bool ( expected) ;
494+ let value_const = self . use_ecx ( location, |this| this. ecx . read_scalar ( & value) ) ?;
495+
496+ if expected != value_const {
497+ // Poison all places this operand references so that further code
498+ // doesn't use the invalid value
499+ match cond {
500+ Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
501+ Self :: remove_const ( & mut self . ecx , place. local ) ;
502+ }
503+ Operand :: Constant ( _) => { }
504+ }
505+ enum DbgVal < T > {
506+ Val ( T ) ,
507+ Underscore ,
508+ }
509+ impl < T : std:: fmt:: Debug > std:: fmt:: Debug for DbgVal < T > {
510+ fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
511+ match self {
512+ Self :: Val ( val) => val. fmt ( fmt) ,
513+ Self :: Underscore => fmt. write_str ( "_" ) ,
514+ }
515+ }
516+ }
517+ let mut eval_to_int = |op| {
518+ // This can be `None` if the lhs wasn't const propagated and we just
519+ // triggered the assert on the value of the rhs.
520+ self . eval_operand ( op, location)
521+ . and_then ( |op| self . ecx . read_immediate ( & op) . ok ( ) )
522+ . map_or ( DbgVal :: Underscore , |op| DbgVal :: Val ( op. to_const_int ( ) ) )
523+ } ;
524+ let msg = match msg {
525+ AssertKind :: DivisionByZero ( op) => AssertKind :: DivisionByZero ( eval_to_int ( op) ) ,
526+ AssertKind :: RemainderByZero ( op) => AssertKind :: RemainderByZero ( eval_to_int ( op) ) ,
527+ AssertKind :: Overflow ( bin_op @ ( BinOp :: Div | BinOp :: Rem ) , op1, op2) => {
528+ // Division overflow is *UB* in the MIR, and different than the
529+ // other overflow checks.
530+ AssertKind :: Overflow ( * bin_op, eval_to_int ( op1) , eval_to_int ( op2) )
531+ }
532+ AssertKind :: BoundsCheck { ref len, ref index } => {
533+ let len = eval_to_int ( len) ;
534+ let index = eval_to_int ( index) ;
535+ AssertKind :: BoundsCheck { len, index }
536+ }
537+ // Remaining overflow errors are already covered by checks on the binary operators.
538+ AssertKind :: Overflow ( ..) | AssertKind :: OverflowNeg ( _) => return None ,
539+ // Need proper const propagator for these.
540+ _ => return None ,
541+ } ;
542+ self . report_assert_as_lint (
543+ lint:: builtin:: UNCONDITIONAL_PANIC ,
544+ location,
545+ "this operation will panic at runtime" ,
546+ msg,
547+ ) ;
548+ }
549+
550+ None
551+ }
552+
483553 fn ensure_not_propagated ( & self , local : Local ) {
484554 if cfg ! ( debug_assertions) {
485555 assert ! (
@@ -585,78 +655,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
585655 self . super_terminator ( terminator, location) ;
586656 match & terminator. kind {
587657 TerminatorKind :: Assert { expected, ref msg, ref cond, .. } => {
588- if let Some ( ref value) = self . eval_operand ( & cond, location) {
589- trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
590- let expected = Scalar :: from_bool ( * expected) ;
591- let Ok ( value_const) = self . ecx . read_scalar ( & value) else {
592- // FIXME should be used use_ecx rather than a local match... but we have
593- // quite a few of these read_scalar/read_immediate that need fixing.
594- return
595- } ;
596- if expected != value_const {
597- enum DbgVal < T > {
598- Val ( T ) ,
599- Underscore ,
600- }
601- impl < T : std:: fmt:: Debug > std:: fmt:: Debug for DbgVal < T > {
602- fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
603- match self {
604- Self :: Val ( val) => val. fmt ( fmt) ,
605- Self :: Underscore => fmt. write_str ( "_" ) ,
606- }
607- }
608- }
609- let mut eval_to_int = |op| {
610- // This can be `None` if the lhs wasn't const propagated and we just
611- // triggered the assert on the value of the rhs.
612- self . eval_operand ( op, location)
613- . and_then ( |op| self . ecx . read_immediate ( & op) . ok ( ) )
614- . map_or ( DbgVal :: Underscore , |op| DbgVal :: Val ( op. to_const_int ( ) ) )
615- } ;
616- let msg = match msg {
617- AssertKind :: DivisionByZero ( op) => {
618- Some ( AssertKind :: DivisionByZero ( eval_to_int ( op) ) )
619- }
620- AssertKind :: RemainderByZero ( op) => {
621- Some ( AssertKind :: RemainderByZero ( eval_to_int ( op) ) )
622- }
623- AssertKind :: Overflow ( bin_op @ ( BinOp :: Div | BinOp :: Rem ) , op1, op2) => {
624- // Division overflow is *UB* in the MIR, and different than the
625- // other overflow checks.
626- Some ( AssertKind :: Overflow (
627- * bin_op,
628- eval_to_int ( op1) ,
629- eval_to_int ( op2) ,
630- ) )
631- }
632- AssertKind :: BoundsCheck { ref len, ref index } => {
633- let len = eval_to_int ( len) ;
634- let index = eval_to_int ( index) ;
635- Some ( AssertKind :: BoundsCheck { len, index } )
636- }
637- // Remaining overflow errors are already covered by checks on the binary operators.
638- AssertKind :: Overflow ( ..) | AssertKind :: OverflowNeg ( _) => None ,
639- // Need proper const propagator for these.
640- _ => None ,
641- } ;
642- // Poison all places this operand references so that further code
643- // doesn't use the invalid value
644- match cond {
645- Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
646- Self :: remove_const ( & mut self . ecx , place. local ) ;
647- }
648- Operand :: Constant ( _) => { }
649- }
650- if let Some ( msg) = msg {
651- self . report_assert_as_lint (
652- lint:: builtin:: UNCONDITIONAL_PANIC ,
653- location,
654- "this operation will panic at runtime" ,
655- msg,
656- ) ;
657- }
658- }
659- }
658+ self . check_assertion ( * expected, msg, cond, location) ;
660659 }
661660 // None of these have Operands to const-propagate.
662661 TerminatorKind :: Goto { .. }
0 commit comments