@@ -10,7 +10,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item
10
10
use rustc_lexer:: tokenize;
11
11
use rustc_lint:: LateContext ;
12
12
use rustc_middle:: mir:: interpret:: { alloc_range, Scalar } ;
13
- use rustc_middle:: ty:: { self , EarlyBinder , FloatTy , GenericArgsRef , List , ScalarInt , Ty , TyCtxt } ;
13
+ use rustc_middle:: ty:: { self , EarlyBinder , FloatTy , GenericArgsRef , IntTy , List , ScalarInt , Ty , TyCtxt , UintTy } ;
14
14
use rustc_middle:: { bug, mir, span_bug} ;
15
15
use rustc_span:: symbol:: { Ident , Symbol } ;
16
16
use rustc_span:: SyntaxContext ;
@@ -51,6 +51,63 @@ pub enum Constant<'tcx> {
51
51
Err ,
52
52
}
53
53
54
+ trait IntTypeBounds : Sized {
55
+ type Output : PartialOrd ;
56
+
57
+ fn min_max ( self ) -> Option < ( Self :: Output , Self :: Output ) > ;
58
+ fn bits ( self ) -> Self :: Output ;
59
+ fn ensure_fits ( self , val : Self :: Output ) -> Option < Self :: Output > {
60
+ let ( min, max) = self . min_max ( ) ?;
61
+ ( min <= val && val <= max) . then_some ( val)
62
+ }
63
+ }
64
+ impl IntTypeBounds for UintTy {
65
+ type Output = u128 ;
66
+ fn min_max ( self ) -> Option < ( Self :: Output , Self :: Output ) > {
67
+ Some ( match self {
68
+ UintTy :: U8 => ( u8:: MIN . into ( ) , u8:: MAX . into ( ) ) ,
69
+ UintTy :: U16 => ( u16:: MIN . into ( ) , u16:: MAX . into ( ) ) ,
70
+ UintTy :: U32 => ( u32:: MIN . into ( ) , u32:: MAX . into ( ) ) ,
71
+ UintTy :: U64 => ( u64:: MIN . into ( ) , u64:: MAX . into ( ) ) ,
72
+ UintTy :: U128 => ( u128:: MIN , u128:: MAX ) ,
73
+ UintTy :: Usize => ( usize:: MIN . try_into ( ) . ok ( ) ?, usize:: MAX . try_into ( ) . ok ( ) ?) ,
74
+ } )
75
+ }
76
+ fn bits ( self ) -> Self :: Output {
77
+ match self {
78
+ UintTy :: U8 => 8 ,
79
+ UintTy :: U16 => 16 ,
80
+ UintTy :: U32 => 32 ,
81
+ UintTy :: U64 => 64 ,
82
+ UintTy :: U128 => 128 ,
83
+ UintTy :: Usize => usize:: BITS . into ( ) ,
84
+ }
85
+ }
86
+ }
87
+ impl IntTypeBounds for IntTy {
88
+ type Output = i128 ;
89
+ fn min_max ( self ) -> Option < ( Self :: Output , Self :: Output ) > {
90
+ Some ( match self {
91
+ IntTy :: I8 => ( i8:: MIN . into ( ) , i8:: MAX . into ( ) ) ,
92
+ IntTy :: I16 => ( i16:: MIN . into ( ) , i16:: MAX . into ( ) ) ,
93
+ IntTy :: I32 => ( i32:: MIN . into ( ) , i32:: MAX . into ( ) ) ,
94
+ IntTy :: I64 => ( i64:: MIN . into ( ) , i64:: MAX . into ( ) ) ,
95
+ IntTy :: I128 => ( i128:: MIN , i128:: MAX ) ,
96
+ IntTy :: Isize => ( isize:: MIN . try_into ( ) . ok ( ) ?, isize:: MAX . try_into ( ) . ok ( ) ?) ,
97
+ } )
98
+ }
99
+ fn bits ( self ) -> Self :: Output {
100
+ match self {
101
+ IntTy :: I8 => 8 ,
102
+ IntTy :: I16 => 16 ,
103
+ IntTy :: I32 => 32 ,
104
+ IntTy :: I64 => 64 ,
105
+ IntTy :: I128 => 128 ,
106
+ IntTy :: Isize => isize:: BITS . into ( ) ,
107
+ }
108
+ }
109
+ }
110
+
54
111
impl < ' tcx > PartialEq for Constant < ' tcx > {
55
112
fn eq ( & self , other : & Self ) -> bool {
56
113
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 panics.
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) ) )
@@ -572,17 +636,33 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
572
636
match ( l, r) {
573
637
( Constant :: Int ( l) , Some ( Constant :: Int ( r) ) ) => match * self . typeck_results . expr_ty_opt ( left) ?. kind ( ) {
574
638
ty:: Int ( ity) => {
639
+ let ( ty_min_value, _) = ity. min_max ( ) ?;
640
+ let bits = ity. bits ( ) ;
575
641
let l = sext ( self . lcx . tcx , l, ity) ;
576
642
let r = sext ( self . lcx . tcx , r, ity) ;
643
+
644
+ // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
645
+ // the right-hand argument is -1 always panics, even with overflow-checks disabled
646
+ if let BinOpKind :: Div | BinOpKind :: Rem = op. node
647
+ && l == ty_min_value
648
+ && r == -1
649
+ {
650
+ return None ;
651
+ }
652
+
577
653
let zext = |n : i128 | Constant :: Int ( unsext ( self . lcx . tcx , n, ity) ) ;
578
654
match op. node {
579
- BinOpKind :: Add => l. checked_add ( r) . map ( zext) ,
580
- BinOpKind :: Sub => l. checked_sub ( r) . map ( zext) ,
581
- BinOpKind :: Mul => l. checked_mul ( r) . map ( zext) ,
655
+ // When +, * or binary - create a value greater than the maximum value, or less than
656
+ // the minimum value that can be stored, it panics.
657
+ BinOpKind :: Add => l. checked_add ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
658
+ BinOpKind :: Sub => l. checked_sub ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
659
+ BinOpKind :: Mul => l. checked_mul ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
582
660
BinOpKind :: Div if r != 0 => l. checked_div ( r) . map ( zext) ,
583
661
BinOpKind :: Rem if r != 0 => l. checked_rem ( r) . map ( zext) ,
584
- BinOpKind :: Shr => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
585
- BinOpKind :: Shl => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
662
+ // Using << or >> where the right-hand argument is greater than or equal to the number of bits
663
+ // in the type of the left-hand argument, or is negative panics.
664
+ BinOpKind :: Shr if r < bits && !r. is_negative ( ) => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
665
+ BinOpKind :: Shl if r < bits && !r. is_negative ( ) => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
586
666
BinOpKind :: BitXor => Some ( zext ( l ^ r) ) ,
587
667
BinOpKind :: BitOr => Some ( zext ( l | r) ) ,
588
668
BinOpKind :: BitAnd => Some ( zext ( l & r) ) ,
@@ -595,24 +675,28 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
595
675
_ => None ,
596
676
}
597
677
} ,
598
- ty:: Uint ( _) => match op. node {
599
- BinOpKind :: Add => l. checked_add ( r) . map ( Constant :: Int ) ,
600
- BinOpKind :: Sub => l. checked_sub ( r) . map ( Constant :: Int ) ,
601
- BinOpKind :: Mul => l. checked_mul ( r) . map ( Constant :: Int ) ,
602
- BinOpKind :: Div => l. checked_div ( r) . map ( Constant :: Int ) ,
603
- BinOpKind :: Rem => l. checked_rem ( r) . map ( Constant :: Int ) ,
604
- BinOpKind :: Shr => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
605
- BinOpKind :: Shl => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
606
- BinOpKind :: BitXor => Some ( Constant :: Int ( l ^ r) ) ,
607
- BinOpKind :: BitOr => Some ( Constant :: Int ( l | r) ) ,
608
- BinOpKind :: BitAnd => Some ( Constant :: Int ( l & r) ) ,
609
- BinOpKind :: Eq => Some ( Constant :: Bool ( l == r) ) ,
610
- BinOpKind :: Ne => Some ( Constant :: Bool ( l != r) ) ,
611
- BinOpKind :: Lt => Some ( Constant :: Bool ( l < r) ) ,
612
- BinOpKind :: Le => Some ( Constant :: Bool ( l <= r) ) ,
613
- BinOpKind :: Ge => Some ( Constant :: Bool ( l >= r) ) ,
614
- BinOpKind :: Gt => Some ( Constant :: Bool ( l > r) ) ,
615
- _ => None ,
678
+ ty:: Uint ( ity) => {
679
+ let bits = ity. bits ( ) ;
680
+
681
+ match op. node {
682
+ BinOpKind :: Add => l. checked_add ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
683
+ BinOpKind :: Sub => l. checked_sub ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
684
+ BinOpKind :: Mul => l. checked_mul ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
685
+ BinOpKind :: Div => l. checked_div ( r) . map ( Constant :: Int ) ,
686
+ BinOpKind :: Rem => l. checked_rem ( r) . map ( Constant :: Int ) ,
687
+ BinOpKind :: Shr if r < bits => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
688
+ BinOpKind :: Shl if r < bits => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
689
+ BinOpKind :: BitXor => Some ( Constant :: Int ( l ^ r) ) ,
690
+ BinOpKind :: BitOr => Some ( Constant :: Int ( l | r) ) ,
691
+ BinOpKind :: BitAnd => Some ( Constant :: Int ( l & r) ) ,
692
+ BinOpKind :: Eq => Some ( Constant :: Bool ( l == r) ) ,
693
+ BinOpKind :: Ne => Some ( Constant :: Bool ( l != r) ) ,
694
+ BinOpKind :: Lt => Some ( Constant :: Bool ( l < r) ) ,
695
+ BinOpKind :: Le => Some ( Constant :: Bool ( l <= r) ) ,
696
+ BinOpKind :: Ge => Some ( Constant :: Bool ( l >= r) ) ,
697
+ BinOpKind :: Gt => Some ( Constant :: Bool ( l > r) ) ,
698
+ _ => None ,
699
+ }
616
700
} ,
617
701
_ => None ,
618
702
} ,
0 commit comments