@@ -12,10 +12,10 @@ use rustc::mir::visit::{
12
12
MutVisitor , MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor ,
13
13
} ;
14
14
use rustc:: mir:: {
15
- read_only, AggregateKind , BasicBlock , BinOp , Body , BodyAndCache , ClearCrossCrate , Constant ,
16
- Local , LocalDecl , LocalKind , Location , Operand , Place , PlaceBase , ReadOnlyBodyAndCache , Rvalue ,
17
- SourceInfo , SourceScope , SourceScopeData , Statement , StatementKind , Terminator , TerminatorKind ,
18
- UnOp , RETURN_PLACE ,
15
+ read_only, AggregateKind , BasicBlock , BinOp , Body , BodyAndCache , CastKind , ClearCrossCrate ,
16
+ Constant , Local , LocalDecl , LocalKind , Location , Operand , Place , PlaceBase ,
17
+ ReadOnlyBodyAndCache , Rvalue , SourceInfo , SourceScope , SourceScopeData , Statement ,
18
+ StatementKind , Terminator , TerminatorKind , UnOp , RETURN_PLACE ,
19
19
} ;
20
20
use rustc:: ty:: layout:: {
21
21
HasDataLayout , HasTyCtxt , LayoutError , LayoutOf , Size , TargetDataLayout , TyLayout ,
@@ -29,9 +29,9 @@ use syntax::ast::Mutability;
29
29
30
30
use crate :: const_eval:: error_to_const_error;
31
31
use crate :: interpret:: {
32
- self , intern_const_alloc_recursive, AllocId , Allocation , Frame , ImmTy , Immediate , InterpCx ,
33
- LocalState , LocalValue , Memory , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy , Pointer ,
34
- ScalarMaybeUndef , StackPopCleanup ,
32
+ self , intern_const_alloc_recursive, truncate , AllocId , Allocation , Frame , ImmTy , Immediate ,
33
+ InterpCx , LocalState , LocalValue , Memory , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy ,
34
+ Pointer , ScalarMaybeUndef , StackPopCleanup ,
35
35
} ;
36
36
use crate :: rustc:: ty:: subst:: Subst ;
37
37
use crate :: transform:: { MirPass , MirSource } ;
@@ -469,15 +469,134 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
469
469
}
470
470
}
471
471
472
+ fn check_unary_op ( & mut self , arg : & Operand < ' tcx > , source_info : SourceInfo ) -> Option < ( ) > {
473
+ self . use_ecx ( source_info, |this| {
474
+ let ty = arg. ty ( & this. local_decls , this. tcx ) ;
475
+
476
+ if ty. is_integral ( ) {
477
+ let arg = this. ecx . eval_operand ( arg, None ) ?;
478
+ let prim = this. ecx . read_immediate ( arg) ?;
479
+ // Need to do overflow check here: For actual CTFE, MIR
480
+ // generation emits code that does this before calling the op.
481
+ if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
482
+ throw_panic ! ( OverflowNeg )
483
+ }
484
+ }
485
+
486
+ Ok ( ( ) )
487
+ } ) ?;
488
+
489
+ Some ( ( ) )
490
+ }
491
+
492
+ fn check_binary_op (
493
+ & mut self ,
494
+ op : BinOp ,
495
+ left : & Operand < ' tcx > ,
496
+ right : & Operand < ' tcx > ,
497
+ source_info : SourceInfo ,
498
+ place_layout : TyLayout < ' tcx > ,
499
+ overflow_check : bool ,
500
+ ) -> Option < ( ) > {
501
+ let r = self . use_ecx ( source_info, |this| {
502
+ this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
503
+ } ) ?;
504
+ if op == BinOp :: Shr || op == BinOp :: Shl {
505
+ let left_bits = place_layout. size . bits ( ) ;
506
+ let right_size = r. layout . size ;
507
+ let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
508
+ if r_bits. map_or ( false , |b| b >= left_bits as u128 ) {
509
+ let lint_root = self . lint_root ( source_info) ?;
510
+ let dir = if op == BinOp :: Shr { "right" } else { "left" } ;
511
+ self . tcx . lint_hir (
512
+ :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
513
+ lint_root,
514
+ source_info. span ,
515
+ & format ! ( "attempt to shift {} with overflow" , dir) ,
516
+ ) ;
517
+ return None ;
518
+ }
519
+ }
520
+
521
+ // If overflow checking is enabled (like in debug mode by default),
522
+ // then we'll already catch overflow when we evaluate the `Assert` statement
523
+ // in MIR. However, if overflow checking is disabled, then there won't be any
524
+ // `Assert` statement and so we have to do additional checking here.
525
+ if !overflow_check {
526
+ self . use_ecx ( source_info, |this| {
527
+ let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
528
+ let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( op, l, r) ?;
529
+
530
+ if overflow {
531
+ let err = err_panic ! ( Overflow ( op) ) . into ( ) ;
532
+ return Err ( err) ;
533
+ }
534
+
535
+ Ok ( ( ) )
536
+ } ) ?;
537
+ }
538
+
539
+ Some ( ( ) )
540
+ }
541
+
542
+ fn check_cast (
543
+ & mut self ,
544
+ op : & Operand < ' tcx > ,
545
+ ty : Ty < ' tcx > ,
546
+ source_info : SourceInfo ,
547
+ place_layout : TyLayout < ' tcx > ,
548
+ ) -> Option < ( ) > {
549
+ if !ty. is_integral ( ) || !op. ty ( & self . local_decls , self . tcx ) . is_integral ( ) {
550
+ return Some ( ( ) ) ;
551
+ }
552
+
553
+ let value = self . use_ecx ( source_info, |this| {
554
+ this. ecx . read_immediate ( this. ecx . eval_operand ( op, None ) ?)
555
+ } ) ?;
556
+
557
+ // Do not try to read bits for ZSTs. This can occur when casting an enum with one variant
558
+ // to an integer. Such enums are represented as ZSTs but still have a discriminant value
559
+ // which can be casted.
560
+ if value. layout . is_zst ( ) {
561
+ return Some ( ( ) ) ;
562
+ }
563
+
564
+ let value_size = value. layout . size ;
565
+ let value_bits = value. to_scalar ( ) . and_then ( |r| r. to_bits ( value_size) ) ;
566
+ if let Ok ( value_bits) = value_bits {
567
+ let truncated = truncate ( value_bits, place_layout. size ) ;
568
+ if truncated != value_bits {
569
+ let scope = source_info. scope ;
570
+ let lint_root = match & self . source_scopes [ scope] . local_data {
571
+ ClearCrossCrate :: Set ( data) => data. lint_root ,
572
+ ClearCrossCrate :: Clear => return None ,
573
+ } ;
574
+ self . tcx . lint_hir (
575
+ :: rustc:: lint:: builtin:: CONST_ERR ,
576
+ lint_root,
577
+ source_info. span ,
578
+ & format ! (
579
+ "truncating cast: the value {} requires {} bits but the target type is \
580
+ only {} bits",
581
+ value_bits,
582
+ value_size. bits( ) ,
583
+ place_layout. size. bits( )
584
+ ) ,
585
+ ) ;
586
+ return None ;
587
+ }
588
+ }
589
+
590
+ Some ( ( ) )
591
+ }
592
+
472
593
fn const_prop (
473
594
& mut self ,
474
595
rvalue : & Rvalue < ' tcx > ,
475
596
place_layout : TyLayout < ' tcx > ,
476
597
source_info : SourceInfo ,
477
598
place : & Place < ' tcx > ,
478
599
) -> Option < ( ) > {
479
- let span = source_info. span ;
480
-
481
600
// #66397: Don't try to eval into large places as that can cause an OOM
482
601
if place_layout. size >= Size :: from_bytes ( MAX_ALLOC_LIMIT ) {
483
602
return None ;
@@ -498,66 +617,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
498
617
// if an overflow would occur.
499
618
Rvalue :: UnaryOp ( UnOp :: Neg , arg) if !overflow_check => {
500
619
trace ! ( "checking UnaryOp(op = Neg, arg = {:?})" , arg) ;
501
-
502
- self . use_ecx ( source_info, |this| {
503
- let ty = arg. ty ( & this. local_decls , this. tcx ) ;
504
-
505
- if ty. is_integral ( ) {
506
- let arg = this. ecx . eval_operand ( arg, None ) ?;
507
- let prim = this. ecx . read_immediate ( arg) ?;
508
- // Need to do overflow check here: For actual CTFE, MIR
509
- // generation emits code that does this before calling the op.
510
- if prim. to_bits ( ) ? == ( 1 << ( prim. layout . size . bits ( ) - 1 ) ) {
511
- throw_panic ! ( OverflowNeg )
512
- }
513
- }
514
-
515
- Ok ( ( ) )
516
- } ) ?;
620
+ self . check_unary_op ( arg, source_info) ?;
517
621
}
518
622
519
623
// Additional checking: check for overflows on integer binary operations and report
520
624
// them to the user as lints.
521
625
Rvalue :: BinaryOp ( op, left, right) => {
522
626
trace ! ( "checking BinaryOp(op = {:?}, left = {:?}, right = {:?})" , op, left, right) ;
523
-
524
- let r = self . use_ecx ( source_info, |this| {
525
- this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?)
526
- } ) ?;
527
- if * op == BinOp :: Shr || * op == BinOp :: Shl {
528
- let left_bits = place_layout. size . bits ( ) ;
529
- let right_size = r. layout . size ;
530
- let r_bits = r. to_scalar ( ) . and_then ( |r| r. to_bits ( right_size) ) ;
531
- if r_bits. map_or ( false , |b| b >= left_bits as u128 ) {
532
- let lint_root = self . lint_root ( source_info) ?;
533
- let dir = if * op == BinOp :: Shr { "right" } else { "left" } ;
534
- self . tcx . lint_hir (
535
- :: rustc:: lint:: builtin:: EXCEEDING_BITSHIFTS ,
536
- lint_root,
537
- span,
538
- & format ! ( "attempt to shift {} with overflow" , dir) ,
539
- ) ;
540
- return None ;
541
- }
542
- }
543
-
544
- // If overflow checking is enabled (like in debug mode by default),
545
- // then we'll already catch overflow when we evaluate the `Assert` statement
546
- // in MIR. However, if overflow checking is disabled, then there won't be any
547
- // `Assert` statement and so we have to do additional checking here.
548
- if !overflow_check {
549
- self . use_ecx ( source_info, |this| {
550
- let l = this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ?;
551
- let ( _, overflow, _ty) = this. ecx . overflowing_binary_op ( * op, l, r) ?;
552
-
553
- if overflow {
554
- let err = err_panic ! ( Overflow ( * op) ) . into ( ) ;
555
- return Err ( err) ;
556
- }
557
-
558
- Ok ( ( ) )
559
- } ) ?;
560
- }
627
+ self . check_binary_op ( * op, left, right, source_info, place_layout, overflow_check) ?;
561
628
}
562
629
563
630
// Work around: avoid ICE in miri. FIXME(wesleywiser)
@@ -584,6 +651,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
584
651
}
585
652
}
586
653
654
+ Rvalue :: Cast ( CastKind :: Misc , op, ty) => {
655
+ trace ! ( "checking Cast(Misc, {:?}, {:?})" , op, ty) ;
656
+ self . check_cast ( op, ty, source_info, place_layout) ?;
657
+ }
658
+
587
659
_ => { }
588
660
}
589
661
0 commit comments