@@ -8,7 +8,7 @@ use rustc::hir::def::DefKind;
8
8
use rustc:: hir:: def_id:: DefId ;
9
9
use rustc:: mir:: {
10
10
AggregateKind , Constant , Location , Place , PlaceBase , Body , Operand , Rvalue ,
11
- Local , NullOp , UnOp , StatementKind , Statement , LocalKind ,
11
+ Local , UnOp , StatementKind , Statement , LocalKind ,
12
12
TerminatorKind , Terminator , ClearCrossCrate , SourceInfo , BinOp ,
13
13
SourceScope , SourceScopeLocalData , LocalDecl , BasicBlock ,
14
14
} ;
@@ -118,7 +118,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
118
118
struct ConstPropMachine ;
119
119
120
120
impl < ' mir , ' tcx > interpret:: Machine < ' mir , ' tcx > for ConstPropMachine {
121
- type MemoryKinds = !;
121
+ type MemoryKinds = !;
122
122
type PointerTag = ( ) ;
123
123
type ExtraFnVal = !;
124
124
@@ -434,32 +434,23 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
434
434
) -> Option < Const < ' tcx > > {
435
435
let span = source_info. span ;
436
436
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 ( ) ;
453
438
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) ;
458
452
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| {
463
454
let ty = arg. ty ( & this. local_decls , this. tcx ) ;
464
455
465
456
if ty. is_integral ( ) {
@@ -471,60 +462,70 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
471
462
throw_panic ! ( OverflowNeg )
472
463
}
473
464
}
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
+ }
474
500
}
475
501
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
+ } ) ?;
505
518
}
506
519
}
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
- }
517
520
518
- Ok ( ( ) )
519
- } ) ?;
520
- } else if let Rvalue :: Ref ( _, _, place) = rvalue {
521
- trace ! ( "checking Ref({:?})" , place) ;
521
+ // Work around: avoid ICE in miri.
522
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.
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) ;
528
529
let alive =
529
530
if let LocalValue :: Live ( _) = self . ecx . frame ( ) . locals [ * local] . value {
530
531
true
@@ -535,6 +536,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
535
536
return None ;
536
537
}
537
538
}
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
+ _ => { }
538
548
}
539
549
540
550
self . use_ecx ( source_info, |this| {
0 commit comments