44use std:: borrow:: Cow ;
55use std:: cell:: Cell ;
66
7- use rustc:: mir:: interpret:: { InterpError , InterpResult , Scalar } ;
7+ use rustc:: lint;
8+ use rustc:: mir:: interpret:: { InterpResult , Scalar } ;
89use rustc:: mir:: visit:: {
910 MutVisitor , MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor ,
1011} ;
@@ -292,7 +293,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
292293struct ConstPropagator < ' mir , ' tcx > {
293294 ecx : InterpCx < ' mir , ' tcx , ConstPropMachine > ,
294295 tcx : TyCtxt < ' tcx > ,
295- source : MirSource < ' tcx > ,
296296 can_const_prop : IndexVec < Local , ConstPropMode > ,
297297 param_env : ParamEnv < ' tcx > ,
298298 // FIXME(eddyb) avoid cloning these two fields more than once,
@@ -372,7 +372,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
372372 ConstPropagator {
373373 ecx,
374374 tcx,
375- source,
376375 param_env,
377376 can_const_prop,
378377 // FIXME(eddyb) avoid cloning these two fields more than once,
@@ -501,19 +500,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
501500 }
502501 }
503502
504- fn report_panic_as_lint ( & self , source_info : SourceInfo , panic : AssertKind < u64 > ) -> Option < ( ) > {
505- // Somewhat convoluted way to re-use the CTFE error reporting code.
503+ fn report_assert_as_lint (
504+ & self ,
505+ lint : & ' static lint:: Lint ,
506+ source_info : SourceInfo ,
507+ message : & ' static str ,
508+ panic : AssertKind < u64 > ,
509+ ) -> Option < ( ) > {
506510 let lint_root = self . lint_root ( source_info) ?;
507- let error = InterpError :: MachineStop ( Box :: new ( format ! ( "{:?}" , panic) ) ) ;
508- let mut diagnostic = error_to_const_error ( & self . ecx , error. into ( ) ) ;
509- diagnostic. span = source_info. span ; // fix the span
510- diagnostic. report_as_lint (
511- self . tcx . at ( source_info. span ) ,
512- "this expression will panic at runtime" ,
513- lint_root,
514- None ,
515- ) ;
516- None
511+ self . tcx . struct_span_lint_hir ( lint, lint_root, source_info. span , |lint| {
512+ let mut err = lint. build ( message) ;
513+ err. span_label ( source_info. span , format ! ( "{:?}" , panic) ) ;
514+ err. emit ( )
515+ } ) ;
516+ return None ;
517517 }
518518
519519 fn check_unary_op (
@@ -530,7 +530,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
530530 // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
531531 // appropriate to use.
532532 assert_eq ! ( op, UnOp :: Neg , "Neg is the only UnOp that can overflow" ) ;
533- self . report_panic_as_lint ( source_info, AssertKind :: OverflowNeg ) ?;
533+ self . report_assert_as_lint (
534+ lint:: builtin:: ARITHMETIC_OVERFLOW ,
535+ source_info,
536+ "this arithmetic operation will overflow" ,
537+ AssertKind :: OverflowNeg ,
538+ ) ?;
534539 }
535540
536541 Some ( ( ) )
@@ -542,27 +547,24 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
542547 left : & Operand < ' tcx > ,
543548 right : & Operand < ' tcx > ,
544549 source_info : SourceInfo ,
545- place_layout : TyLayout < ' tcx > ,
546550 ) -> Option < ( ) > {
547551 let r =
548552 self . use_ecx ( |this| this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?) ) ?;
549553 // Check for exceeding shifts *even if* we cannot evaluate the LHS.
550554 if op == BinOp :: Shr || op == BinOp :: Shl {
551- let left_bits = place_layout. size . bits ( ) ;
555+ // We need the type of the LHS. We cannot use `place_layout` as that is the type
556+ // of the result, which for checked binops is not the same!
557+ let left_ty = left. ty ( & self . local_decls , self . tcx ) ;
558+ let left_size_bits = self . ecx . layout_of ( left_ty) . ok ( ) ?. size . bits ( ) ;
552559 let right_size = r. layout . size ;
553560 let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
554- if r_bits. map_or ( false , |b| b >= left_bits as u128 ) {
555- let lint_root = self . lint_root ( source_info) ?;
556- self . tcx . struct_span_lint_hir (
557- :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
558- lint_root,
559- source_info. span ,
560- |lint| {
561- let dir = if op == BinOp :: Shr { "right" } else { "left" } ;
562- lint. build ( & format ! ( "attempt to shift {} with overflow" , dir) ) . emit ( )
563- } ,
564- ) ;
565- return None ;
561+ if r_bits. map_or ( false , |b| b >= left_size_bits as u128 ) {
562+ self . report_assert_as_lint (
563+ lint:: builtin:: ARITHMETIC_OVERFLOW ,
564+ source_info,
565+ "this arithmetic operation will overflow" ,
566+ AssertKind :: Overflow ( op) ,
567+ ) ?;
566568 }
567569 }
568570
@@ -572,7 +574,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
572574 let ( _res, overflow, _ty) = this. ecx . overflowing_binary_op ( op, l, r) ?;
573575 Ok ( overflow)
574576 } ) ? {
575- self . report_panic_as_lint ( source_info, AssertKind :: Overflow ( op) ) ?;
577+ self . report_assert_as_lint (
578+ lint:: builtin:: ARITHMETIC_OVERFLOW ,
579+ source_info,
580+ "this arithmetic operation will overflow" ,
581+ AssertKind :: Overflow ( op) ,
582+ ) ?;
576583 }
577584
578585 Some ( ( ) )
@@ -595,8 +602,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
595602 return None ;
596603 }
597604
598- let overflow_check = self . tcx . sess . overflow_checks ( ) ;
599-
600605 // Perform any special handling for specific Rvalue types.
601606 // Generally, checks here fall into one of two categories:
602607 // 1. Additional checking to provide useful lints to the user
@@ -606,20 +611,25 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
606611 // - In this case, we'll return `None` from this function to stop evaluation.
607612 match rvalue {
608613 // Additional checking: give lints to the user if an overflow would occur.
609- // If `overflow_check` is set, running const-prop on the `Assert` terminators
610- // will already generate the appropriate messages.
611- Rvalue :: UnaryOp ( op, arg) if !overflow_check => {
614+ // We do this here and not in the `Assert` terminator as that terminator is
615+ // only sometimes emitted (overflow checks can be disabled), but we want to always
616+ // lint.
617+ Rvalue :: UnaryOp ( op, arg) => {
612618 trace ! ( "checking UnaryOp(op = {:?}, arg = {:?})" , op, arg) ;
613619 self . check_unary_op ( * op, arg, source_info) ?;
614620 }
615-
616- // Additional checking: check for overflows on integer binary operations and report
617- // them to the user as lints.
618- // If `overflow_check` is set, running const-prop on the `Assert` terminators
619- // will already generate the appropriate messages.
620- Rvalue :: BinaryOp ( op, left, right) if !overflow_check => {
621+ Rvalue :: BinaryOp ( op, left, right) => {
621622 trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
622- self . check_binary_op ( * op, left, right, source_info, place_layout) ?;
623+ self . check_binary_op ( * op, left, right, source_info) ?;
624+ }
625+ Rvalue :: CheckedBinaryOp ( op, left, right) => {
626+ trace ! (
627+ "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})" ,
628+ op,
629+ left,
630+ right
631+ ) ;
632+ self . check_binary_op ( * op, left, right, source_info) ?;
623633 }
624634
625635 // Do not try creating references (#67862)
@@ -898,54 +908,39 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
898908 }
899909 Operand :: Constant ( _) => { }
900910 }
901- let span = terminator. source_info . span ;
902- let hir_id = self
903- . tcx
904- . hir ( )
905- . as_local_hir_id ( self . source . def_id ( ) )
906- . expect ( "some part of a failing const eval must be local" ) ;
907- self . tcx . struct_span_lint_hir (
908- :: rustc:: lint:: builtin:: CONST_ERR ,
909- hir_id,
910- span,
911- |lint| {
912- let msg = match msg {
913- AssertKind :: Overflow ( _)
914- | AssertKind :: OverflowNeg
915- | AssertKind :: DivisionByZero
916- | AssertKind :: RemainderByZero => msg. description ( ) . to_owned ( ) ,
917- AssertKind :: BoundsCheck { ref len, ref index } => {
918- let len = self
919- . eval_operand ( len, source_info)
920- . expect ( "len must be const" ) ;
921- let len = match self . ecx . read_scalar ( len) {
922- Ok ( ScalarMaybeUndef :: Scalar ( Scalar :: Raw {
923- data,
924- ..
925- } ) ) => data,
926- other => bug ! ( "const len not primitive: {:?}" , other) ,
927- } ;
928- let index = self
929- . eval_operand ( index, source_info)
930- . expect ( "index must be const" ) ;
931- let index = match self . ecx . read_scalar ( index) {
932- Ok ( ScalarMaybeUndef :: Scalar ( Scalar :: Raw {
933- data,
934- ..
935- } ) ) => data,
936- other => bug ! ( "const index not primitive: {:?}" , other) ,
937- } ;
938- format ! (
939- "index out of bounds: \
940- the len is {} but the index is {}",
941- len, index,
942- )
943- }
944- // Need proper const propagator for these
945- _ => return ,
946- } ;
947- lint. build ( & msg) . emit ( )
948- } ,
911+ let msg = match msg {
912+ AssertKind :: DivisionByZero => AssertKind :: DivisionByZero ,
913+ AssertKind :: RemainderByZero => AssertKind :: RemainderByZero ,
914+ AssertKind :: BoundsCheck { ref len, ref index } => {
915+ let len =
916+ self . eval_operand ( len, source_info) . expect ( "len must be const" ) ;
917+ let len = self
918+ . ecx
919+ . read_scalar ( len)
920+ . unwrap ( )
921+ . to_machine_usize ( & self . tcx )
922+ . unwrap ( ) ;
923+ let index = self
924+ . eval_operand ( index, source_info)
925+ . expect ( "index must be const" ) ;
926+ let index = self
927+ . ecx
928+ . read_scalar ( index)
929+ . unwrap ( )
930+ . to_machine_usize ( & self . tcx )
931+ . unwrap ( ) ;
932+ AssertKind :: BoundsCheck { len, index }
933+ }
934+ // Overflow is are already covered by checks on the binary operators.
935+ AssertKind :: Overflow ( _) | AssertKind :: OverflowNeg => return ,
936+ // Need proper const propagator for these.
937+ _ => return ,
938+ } ;
939+ self . report_assert_as_lint (
940+ lint:: builtin:: UNCONDITIONAL_PANIC ,
941+ source_info,
942+ "this operation will panic at runtime" ,
943+ msg,
949944 ) ;
950945 } else {
951946 if self . should_const_prop ( value) {
0 commit comments