@@ -434,33 +434,41 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
434
434
) -> Option < Const < ' tcx > > {
435
435
let span = source_info. span ;
436
436
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.
438
446
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 => {
440
451
trace ! ( "checking UnaryOp(op = Neg, arg = {:?})" , arg) ;
441
- let overflow_check = self . tcx . sess . overflow_checks ( ) ;
442
452
443
453
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 )
457
463
}
458
464
}
459
465
460
466
Ok ( ( ) )
461
467
} ) ?;
462
468
}
463
469
470
+ // Additional checking: check for overflows on integer binary operations and report
471
+ // them to the user as lints.
464
472
Rvalue :: BinaryOp ( op, left, right) => {
465
473
trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
466
474
@@ -490,25 +498,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
490
498
return None ;
491
499
}
492
500
}
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
- }
503
501
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
+ }
506
519
}
507
520
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.
508
527
Rvalue :: Ref ( _, _, Place { base : PlaceBase :: Local ( local) , projection : box [ ] } ) => {
509
528
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.
512
529
let alive =
513
530
if let LocalValue :: Live ( _) = self . ecx . frame ( ) . locals [ * local] . value {
514
531
true
@@ -520,9 +537,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
520
537
}
521
538
}
522
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.
523
543
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.
526
544
return None ;
527
545
}
528
546
0 commit comments