@@ -8,7 +8,7 @@ use rustc::hir::def::DefKind;
88use rustc:: hir:: def_id:: DefId ;
99use rustc:: mir:: {
1010 AggregateKind , Constant , Location , Place , PlaceBase , Body , Operand , Rvalue ,
11- Local , NullOp , UnOp , StatementKind , Statement , LocalKind ,
11+ Local , UnOp , StatementKind , Statement , LocalKind ,
1212 TerminatorKind , Terminator , ClearCrossCrate , SourceInfo , BinOp ,
1313 SourceScope , SourceScopeLocalData , LocalDecl , BasicBlock ,
1414} ;
@@ -118,7 +118,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
118118struct ConstPropMachine ;
119119
120120impl < ' mir , ' tcx > interpret:: Machine < ' mir , ' tcx > for ConstPropMachine {
121- type MemoryKinds = !;
121+ type MemoryKinds = !;
122122 type PointerTag = ( ) ;
123123 type ExtraFnVal = !;
124124
@@ -434,32 +434,23 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
434434 ) -> Option < Const < ' tcx > > {
435435 let span = source_info. span ;
436436
437- // if this isn't a supported operation, then return None
438- match rvalue {
439- Rvalue :: Repeat ( ..) |
440- Rvalue :: Aggregate ( ..) |
441- Rvalue :: NullaryOp ( NullOp :: Box , _) |
442- Rvalue :: Discriminant ( ..) => return None ,
443-
444- Rvalue :: Use ( _) |
445- Rvalue :: Len ( _) |
446- Rvalue :: Cast ( ..) |
447- Rvalue :: NullaryOp ( ..) |
448- Rvalue :: CheckedBinaryOp ( ..) |
449- Rvalue :: Ref ( ..) |
450- Rvalue :: UnaryOp ( ..) |
451- Rvalue :: BinaryOp ( ..) => { }
452- }
437+ let overflow_check = self . tcx . sess . overflow_checks ( ) ;
453438
454- // perform any special checking for specific Rvalue types
455- if let Rvalue :: UnaryOp ( op, arg) = rvalue {
456- trace ! ( "checking UnaryOp(op = {:?}, arg = {:?})" , op, arg) ;
457- let overflow_check = self . tcx . sess . overflow_checks ( ) ;
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.
446+ match rvalue {
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 => {
451+ trace ! ( "checking UnaryOp(op = Neg, arg = {:?})" , arg) ;
458452
459- self . use_ecx ( source_info, |this| {
460- // We check overflow in debug mode already
461- // so should only check in release mode.
462- if * op == UnOp :: Neg && !overflow_check {
453+ self . use_ecx ( source_info, |this| {
463454 let ty = arg. ty ( & this. local_decls , this. tcx ) ;
464455
465456 if ty. is_integral ( ) {
@@ -471,60 +462,70 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
471462 throw_panic ! ( OverflowNeg )
472463 }
473464 }
465+
466+ Ok ( ( ) )
467+ } ) ?;
468+ }
469+
470+ // Additional checking: check for overflows on integer binary operations and report
471+ // them to the user as lints.
472+ Rvalue :: BinaryOp ( op, left, right) => {
473+ trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
474+
475+ let r = self . use_ecx ( source_info, |this| {
476+ this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
477+ } ) ?;
478+ if * op == BinOp :: Shr || * op == BinOp :: Shl {
479+ let left_bits = place_layout. size . bits ( ) ;
480+ let right_size = r. layout . size ;
481+ let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
482+ if r_bits. ok ( ) . map_or ( false , |b| b >= left_bits as u128 ) {
483+ let source_scope_local_data = match self . source_scope_local_data {
484+ ClearCrossCrate :: Set ( ref data) => data,
485+ ClearCrossCrate :: Clear => return None ,
486+ } ;
487+ let dir = if * op == BinOp :: Shr {
488+ "right"
489+ } else {
490+ "left"
491+ } ;
492+ let hir_id = source_scope_local_data[ source_info. scope ] . lint_root ;
493+ self . tcx . lint_hir (
494+ :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
495+ hir_id,
496+ span,
497+ & format ! ( "attempt to shift {} with overflow" , dir) ) ;
498+ return None ;
499+ }
474500 }
475501
476- Ok ( ( ) )
477- } ) ?;
478- } else if let Rvalue :: BinaryOp ( op, left, right) = rvalue {
479- trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
480-
481- let r = self . use_ecx ( source_info, |this| {
482- this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
483- } ) ?;
484- if * op == BinOp :: Shr || * op == BinOp :: Shl {
485- let left_bits = place_layout. size . bits ( ) ;
486- let right_size = r. layout . size ;
487- let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
488- if r_bits. ok ( ) . map_or ( false , |b| b >= left_bits as u128 ) {
489- let source_scope_local_data = match self . source_scope_local_data {
490- ClearCrossCrate :: Set ( ref data) => data,
491- ClearCrossCrate :: Clear => return None ,
492- } ;
493- let dir = if * op == BinOp :: Shr {
494- "right"
495- } else {
496- "left"
497- } ;
498- let hir_id = source_scope_local_data[ source_info. scope ] . lint_root ;
499- self . tcx . lint_hir (
500- :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
501- hir_id,
502- span,
503- & format ! ( "attempt to shift {} with overflow" , dir) ) ;
504- return None ;
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+ } ) ?;
505518 }
506519 }
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- // We check overflow in debug mode already
512- // so should only check in release mode.
513- if !this. tcx . sess . overflow_checks ( ) && overflow {
514- let err = err_panic ! ( Overflow ( * op) ) . into ( ) ;
515- return Err ( err) ;
516- }
517520
518- Ok ( ( ) )
519- } ) ?;
520- } else if let Rvalue :: Ref ( _, _, place) = rvalue {
521- trace ! ( "checking Ref({:?})" , place) ;
521+ // Work around: avoid ICE in miri.
522522 // 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.
524- if let Place {
525- base : PlaceBase :: Local ( local) ,
526- projection : box [ ]
527- } = place {
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.
527+ Rvalue :: Ref ( _, _, Place { base : PlaceBase :: Local ( local) , projection : box [ ] } ) => {
528+ trace ! ( "checking Ref({:?})" , place) ;
528529 let alive =
529530 if let LocalValue :: Live ( _) = self . ecx . frame ( ) . locals [ * local] . value {
530531 true
@@ -535,6 +536,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
535536 return None ;
536537 }
537538 }
539+
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.
543+ Rvalue :: Aggregate ( _, operands) if operands. len ( ) == 0 => {
544+ return None ;
545+ }
546+
547+ _ => { }
538548 }
539549
540550 self . use_ecx ( source_info, |this| {
0 commit comments