@@ -434,33 +434,41 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
434434 ) -> Option < Const < ' tcx > > {
435435 let span = source_info. span ;
436436
437- // perform any special checking for specific Rvalue types
437+ let overflow_check = self . tcx . sess . overflow_checks ( ) ;
438+
439+ // Perform any special handling for specific Rvalue types.
440+ // Generally, checks here fall into one of two categories:
441+ // 1. Additional checking to provide useful lints to the user
442+ // - In this case, we will do some validation and then fall through to the
443+ // end of the function which evals the assignment.
444+ // 2. Working around bugs in other parts of the compiler
445+ // - In this case, we'll return `None` from this function to stop evaluation.
438446 match rvalue {
439- Rvalue :: UnaryOp ( UnOp :: Neg , arg) => {
447+ // Additional checking: if overflow checks are disabled (which is usually the case in
448+ // release mode), then we need to do additional checking here to give lints to the user
449+ // if an overflow would occur.
450+ Rvalue :: UnaryOp ( UnOp :: Neg , arg) if !overflow_check => {
440451 trace ! ( "checking UnaryOp(op = Neg, arg = {:?})" , arg) ;
441- let overflow_check = self . tcx . sess . overflow_checks ( ) ;
442452
443453 self . use_ecx ( source_info, |this| {
444- // We check overflow in debug mode already
445- // so should only check in release mode.
446- if !overflow_check {
447- let ty = arg. ty ( & this. local_decls , this. tcx ) ;
448-
449- if ty. is_integral ( ) {
450- let arg = this. ecx . eval_operand ( arg, None ) ?;
451- let prim = this. ecx . read_immediate ( arg) ?;
452- // Need to do overflow check here: For actual CTFE, MIR
453- // generation emits code that does this before calling the op.
454- if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
455- throw_panic ! ( OverflowNeg )
456- }
454+ let ty = arg. ty ( & this. local_decls , this. tcx ) ;
455+
456+ if ty. is_integral ( ) {
457+ let arg = this. ecx . eval_operand ( arg, None ) ?;
458+ let prim = this. ecx . read_immediate ( arg) ?;
459+ // Need to do overflow check here: For actual CTFE, MIR
460+ // generation emits code that does this before calling the op.
461+ if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
462+ throw_panic ! ( OverflowNeg )
457463 }
458464 }
459465
460466 Ok ( ( ) )
461467 } ) ?;
462468 }
463469
470+ // Additional checking: check for overflows on integer binary operations and report
471+ // them to the user as lints.
464472 Rvalue :: BinaryOp ( op, left, right) => {
465473 trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
466474
@@ -490,25 +498,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
490498 return None ;
491499 }
492500 }
493- self . use_ecx ( source_info, |this| {
494- let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
495- let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( * op, l, r) ?;
496-
497- // We check overflow in debug mode already
498- // so should only check in release mode.
499- if !this. tcx . sess . overflow_checks ( ) && overflow {
500- let err = err_panic ! ( Overflow ( * op) ) . into ( ) ;
501- return Err ( err) ;
502- }
503501
504- Ok ( ( ) )
505- } ) ?;
502+ // If overflow checking is enabled (like in debug mode by default),
503+ // then we'll already catch overflow when we evaluate the `Assert` statement
504+ // in MIR. However, if overflow checking is disabled, then there won't be any
505+ // `Assert` statement and so we have to do additional checking here.
506+ if !overflow_check {
507+ self . use_ecx ( source_info, |this| {
508+ let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
509+ let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( * op, l, r) ?;
510+
511+ if overflow {
512+ let err = err_panic ! ( Overflow ( * op) ) . into ( ) ;
513+ return Err ( err) ;
514+ }
515+
516+ Ok ( ( ) )
517+ } ) ?;
518+ }
506519 }
507520
521+ // Work around: avoid ICE in miri.
522+ // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
523+ // from a function argument that hasn't been assigned to in this function. The main
524+ // issue is if an arg is a fat-pointer, miri `expects()` to be able to read the value
525+ // of that pointer to get size info. However, since this is `ConstProp`, that argument
526+ // doesn't actually have a backing value and so this causes an ICE.
508527 Rvalue :: Ref ( _, _, Place { base : PlaceBase :: Local ( local) , projection : box [ ] } ) => {
509528 trace ! ( "checking Ref({:?})" , place) ;
510- // FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
511- // from a function argument that hasn't been assigned to in this function.
512529 let alive =
513530 if let LocalValue :: Live ( _) = self . ecx . frame ( ) . locals [ * local] . value {
514531 true
@@ -520,9 +537,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
520537 }
521538 }
522539
540+ // Work around: avoid extra unnecessary locals.
541+ // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that
542+ // `SimplifyLocals` doesn't know it can remove.
523543 Rvalue :: Aggregate ( _, operands) if operands. len ( ) == 0 => {
524- // FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that
525- // `SimplifyLocals` doesn't know it can remove.
526544 return None ;
527545 }
528546
0 commit comments