@@ -11,7 +11,7 @@ use rustc_lexer::tokenize;
11
11
use rustc_lint:: LateContext ;
12
12
use rustc_middle:: mir;
13
13
use rustc_middle:: mir:: interpret:: Scalar ;
14
- use rustc_middle:: ty:: { self , EarlyBinder , FloatTy , ScalarInt , Ty , TyCtxt } ;
14
+ use rustc_middle:: ty:: { self , EarlyBinder , FloatTy , IntTy , ScalarInt , Ty , TyCtxt , UintTy } ;
15
15
use rustc_middle:: ty:: { List , SubstsRef } ;
16
16
use rustc_middle:: { bug, span_bug} ;
17
17
use rustc_span:: symbol:: { Ident , Symbol } ;
@@ -52,6 +52,63 @@ pub enum Constant<'tcx> {
52
52
Err ,
53
53
}
54
54
55
+ trait IntTypeBounds : Sized {
56
+ type Output : PartialOrd ;
57
+
58
+ fn min_max ( self ) -> Option < ( Self :: Output , Self :: Output ) > ;
59
+ fn bits ( self ) -> Self :: Output ;
60
+ fn ensure_fits ( self , val : Self :: Output ) -> Option < Self :: Output > {
61
+ let ( min, max) = self . min_max ( ) ?;
62
+ ( min <= val && val <= max) . then_some ( val)
63
+ }
64
+ }
65
+ impl IntTypeBounds for UintTy {
66
+ type Output = u128 ;
67
+ fn min_max ( self ) -> Option < ( Self :: Output , Self :: Output ) > {
68
+ Some ( match self {
69
+ UintTy :: U8 => ( u8:: MIN . into ( ) , u8:: MAX . into ( ) ) ,
70
+ UintTy :: U16 => ( u16:: MIN . into ( ) , u16:: MAX . into ( ) ) ,
71
+ UintTy :: U32 => ( u32:: MIN . into ( ) , u32:: MAX . into ( ) ) ,
72
+ UintTy :: U64 => ( u64:: MIN . into ( ) , u64:: MAX . into ( ) ) ,
73
+ UintTy :: U128 => ( u128:: MIN , u128:: MAX ) ,
74
+ UintTy :: Usize => ( usize:: MIN . try_into ( ) . ok ( ) ?, usize:: MAX . try_into ( ) . ok ( ) ?) ,
75
+ } )
76
+ }
77
+ fn bits ( self ) -> Self :: Output {
78
+ match self {
79
+ UintTy :: U8 => 8 ,
80
+ UintTy :: U16 => 16 ,
81
+ UintTy :: U32 => 32 ,
82
+ UintTy :: U64 => 64 ,
83
+ UintTy :: U128 => 128 ,
84
+ UintTy :: Usize => usize:: BITS . try_into ( ) . unwrap ( ) ,
85
+ }
86
+ }
87
+ }
88
+ impl IntTypeBounds for IntTy {
89
+ type Output = i128 ;
90
+ fn min_max ( self ) -> Option < ( Self :: Output , Self :: Output ) > {
91
+ Some ( match self {
92
+ IntTy :: I8 => ( i8:: MIN . into ( ) , i8:: MAX . into ( ) ) ,
93
+ IntTy :: I16 => ( i16:: MIN . into ( ) , i16:: MAX . into ( ) ) ,
94
+ IntTy :: I32 => ( i32:: MIN . into ( ) , i32:: MAX . into ( ) ) ,
95
+ IntTy :: I64 => ( i64:: MIN . into ( ) , i64:: MAX . into ( ) ) ,
96
+ IntTy :: I128 => ( i128:: MIN , i128:: MAX ) ,
97
+ IntTy :: Isize => ( isize:: MIN . try_into ( ) . ok ( ) ?, isize:: MAX . try_into ( ) . ok ( ) ?) ,
98
+ } )
99
+ }
100
+ fn bits ( self ) -> Self :: Output {
101
+ match self {
102
+ IntTy :: I8 => 8 ,
103
+ IntTy :: I16 => 16 ,
104
+ IntTy :: I32 => 32 ,
105
+ IntTy :: I64 => 64 ,
106
+ IntTy :: I128 => 128 ,
107
+ IntTy :: Isize => isize:: BITS . try_into ( ) . unwrap ( ) ,
108
+ }
109
+ }
110
+ }
111
+
55
112
impl < ' tcx > PartialEq for Constant < ' tcx > {
56
113
fn eq ( & self , other : & Self ) -> bool {
57
114
match ( self , other) {
@@ -435,8 +492,15 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
435
492
match * o {
436
493
Int ( value) => {
437
494
let ty:: Int ( ity) = * ty. kind ( ) else { return None } ;
495
+ let ( min, _) = ity. min_max ( ) ?;
438
496
// sign extend
439
497
let value = sext ( self . lcx . tcx , value, ity) ;
498
+
499
+ // Applying unary - to the most negative value of any signed integer type panicks.
500
+ if value == min {
501
+ return None ;
502
+ }
503
+
440
504
let value = value. checked_neg ( ) ?;
441
505
// clear unused bits
442
506
Some ( Int ( unsext ( self . lcx . tcx , value, ity) ) )
@@ -568,17 +632,30 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
568
632
match ( l, r) {
569
633
( Constant :: Int ( l) , Some ( Constant :: Int ( r) ) ) => match * self . typeck_results . expr_ty_opt ( left) ?. kind ( ) {
570
634
ty:: Int ( ity) => {
635
+ let ( ty_min_value, _) = ity. min_max ( ) ?;
636
+ let bits = ity. bits ( ) ;
571
637
let l = sext ( self . lcx . tcx , l, ity) ;
572
638
let r = sext ( self . lcx . tcx , r, ity) ;
639
+
640
+ // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
641
+ // the right-hand argument is -1 always panicks, even with overflow-checks disabled
642
+ if l == ty_min_value && r == -1 {
643
+ return None ;
644
+ }
645
+
573
646
let zext = |n : i128 | Constant :: Int ( unsext ( self . lcx . tcx , n, ity) ) ;
574
647
match op. node {
575
- BinOpKind :: Add => l. checked_add ( r) . map ( zext) ,
576
- BinOpKind :: Sub => l. checked_sub ( r) . map ( zext) ,
577
- BinOpKind :: Mul => l. checked_mul ( r) . map ( zext) ,
648
+ // When +, * or binary - create a value greater than the maximum value, or less than
649
+ // the minimum value that can be stored, it panicks.
650
+ BinOpKind :: Add => l. checked_add ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
651
+ BinOpKind :: Sub => l. checked_sub ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
652
+ BinOpKind :: Mul => l. checked_mul ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
578
653
BinOpKind :: Div if r != 0 => l. checked_div ( r) . map ( zext) ,
579
654
BinOpKind :: Rem if r != 0 => l. checked_rem ( r) . map ( zext) ,
580
- BinOpKind :: Shr => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
581
- BinOpKind :: Shl => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
655
+ // Using << or >> where the right-hand argument is greater than or equal to the number of bits
656
+ // in the type of the left-hand argument, or is negative panicks.
657
+ BinOpKind :: Shr if r < bits && !l. is_negative ( ) => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
658
+ BinOpKind :: Shl if r < bits && !l. is_negative ( ) => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
582
659
BinOpKind :: BitXor => Some ( zext ( l ^ r) ) ,
583
660
BinOpKind :: BitOr => Some ( zext ( l | r) ) ,
584
661
BinOpKind :: BitAnd => Some ( zext ( l & r) ) ,
@@ -591,24 +668,28 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
591
668
_ => None ,
592
669
}
593
670
} ,
594
- ty:: Uint ( _) => match op. node {
595
- BinOpKind :: Add => l. checked_add ( r) . map ( Constant :: Int ) ,
596
- BinOpKind :: Sub => l. checked_sub ( r) . map ( Constant :: Int ) ,
597
- BinOpKind :: Mul => l. checked_mul ( r) . map ( Constant :: Int ) ,
598
- BinOpKind :: Div => l. checked_div ( r) . map ( Constant :: Int ) ,
599
- BinOpKind :: Rem => l. checked_rem ( r) . map ( Constant :: Int ) ,
600
- BinOpKind :: Shr => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
601
- BinOpKind :: Shl => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
602
- BinOpKind :: BitXor => Some ( Constant :: Int ( l ^ r) ) ,
603
- BinOpKind :: BitOr => Some ( Constant :: Int ( l | r) ) ,
604
- BinOpKind :: BitAnd => Some ( Constant :: Int ( l & r) ) ,
605
- BinOpKind :: Eq => Some ( Constant :: Bool ( l == r) ) ,
606
- BinOpKind :: Ne => Some ( Constant :: Bool ( l != r) ) ,
607
- BinOpKind :: Lt => Some ( Constant :: Bool ( l < r) ) ,
608
- BinOpKind :: Le => Some ( Constant :: Bool ( l <= r) ) ,
609
- BinOpKind :: Ge => Some ( Constant :: Bool ( l >= r) ) ,
610
- BinOpKind :: Gt => Some ( Constant :: Bool ( l > r) ) ,
611
- _ => None ,
671
+ ty:: Uint ( ity) => {
672
+ let bits = ity. bits ( ) ;
673
+
674
+ match op. node {
675
+ BinOpKind :: Add => l. checked_add ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
676
+ BinOpKind :: Sub => l. checked_sub ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
677
+ BinOpKind :: Mul => l. checked_mul ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
678
+ BinOpKind :: Div => l. checked_div ( r) . map ( Constant :: Int ) ,
679
+ BinOpKind :: Rem => l. checked_rem ( r) . map ( Constant :: Int ) ,
680
+ BinOpKind :: Shr if r < bits => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
681
+ BinOpKind :: Shl if r < bits => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
682
+ BinOpKind :: BitXor => Some ( Constant :: Int ( l ^ r) ) ,
683
+ BinOpKind :: BitOr => Some ( Constant :: Int ( l | r) ) ,
684
+ BinOpKind :: BitAnd => Some ( Constant :: Int ( l & r) ) ,
685
+ BinOpKind :: Eq => Some ( Constant :: Bool ( l == r) ) ,
686
+ BinOpKind :: Ne => Some ( Constant :: Bool ( l != r) ) ,
687
+ BinOpKind :: Lt => Some ( Constant :: Bool ( l < r) ) ,
688
+ BinOpKind :: Le => Some ( Constant :: Bool ( l <= r) ) ,
689
+ BinOpKind :: Ge => Some ( Constant :: Bool ( l >= r) ) ,
690
+ BinOpKind :: Gt => Some ( Constant :: Bool ( l > r) ) ,
691
+ _ => None ,
692
+ }
612
693
} ,
613
694
_ => None ,
614
695
} ,
0 commit comments