@@ -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) {
@@ -433,8 +490,15 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
433
490
match * o {
434
491
Int ( value) => {
435
492
let ty:: Int ( ity) = * ty. kind ( ) else { return None } ;
493
+ let ( min, _) = ity. min_max ( ) ?;
436
494
// sign extend
437
495
let value = sext ( self . lcx . tcx , value, ity) ;
496
+
497
+ // Applying unary - to the most negative value of any signed integer type panics.
498
+ if value == min {
499
+ return None ;
500
+ }
501
+
438
502
let value = value. checked_neg ( ) ?;
439
503
// clear unused bits
440
504
Some ( Int ( unsext ( self . lcx . tcx , value, ity) ) )
@@ -570,17 +634,33 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
570
634
match ( l, r) {
571
635
( Constant :: Int ( l) , Some ( Constant :: Int ( r) ) ) => match * self . typeck_results . expr_ty_opt ( left) ?. kind ( ) {
572
636
ty:: Int ( ity) => {
637
+ let ( ty_min_value, _) = ity. min_max ( ) ?;
638
+ let bits = ity. bits ( ) ;
573
639
let l = sext ( self . lcx . tcx , l, ity) ;
574
640
let r = sext ( self . lcx . tcx , r, ity) ;
641
+
642
+ // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
643
+ // the right-hand argument is -1 always panics, even with overflow-checks disabled
644
+ if let BinOpKind :: Div | BinOpKind :: Rem = op. node
645
+ && l == ty_min_value
646
+ && r == -1
647
+ {
648
+ return None ;
649
+ }
650
+
575
651
let zext = |n : i128 | Constant :: Int ( unsext ( self . lcx . tcx , n, ity) ) ;
576
652
match op. node {
577
- BinOpKind :: Add => l. checked_add ( r) . map ( zext) ,
578
- BinOpKind :: Sub => l. checked_sub ( r) . map ( zext) ,
579
- BinOpKind :: Mul => l. checked_mul ( r) . map ( zext) ,
653
+ // When +, * or binary - create a value greater than the maximum value, or less than
654
+ // the minimum value that can be stored, it panics.
655
+ BinOpKind :: Add => l. checked_add ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
656
+ BinOpKind :: Sub => l. checked_sub ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
657
+ BinOpKind :: Mul => l. checked_mul ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( zext) ,
580
658
BinOpKind :: Div if r != 0 => l. checked_div ( r) . map ( zext) ,
581
659
BinOpKind :: Rem if r != 0 => l. checked_rem ( r) . map ( zext) ,
582
- BinOpKind :: Shr => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
583
- BinOpKind :: Shl => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
660
+ // Using << or >> where the right-hand argument is greater than or equal to the number of bits
661
+ // in the type of the left-hand argument, or is negative panics.
662
+ BinOpKind :: Shr if r < bits && !r. is_negative ( ) => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
663
+ BinOpKind :: Shl if r < bits && !r. is_negative ( ) => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( zext) ,
584
664
BinOpKind :: BitXor => Some ( zext ( l ^ r) ) ,
585
665
BinOpKind :: BitOr => Some ( zext ( l | r) ) ,
586
666
BinOpKind :: BitAnd => Some ( zext ( l & r) ) ,
@@ -593,24 +673,28 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
593
673
_ => None ,
594
674
}
595
675
} ,
596
- ty:: Uint ( _) => match op. node {
597
- BinOpKind :: Add => l. checked_add ( r) . map ( Constant :: Int ) ,
598
- BinOpKind :: Sub => l. checked_sub ( r) . map ( Constant :: Int ) ,
599
- BinOpKind :: Mul => l. checked_mul ( r) . map ( Constant :: Int ) ,
600
- BinOpKind :: Div => l. checked_div ( r) . map ( Constant :: Int ) ,
601
- BinOpKind :: Rem => l. checked_rem ( r) . map ( Constant :: Int ) ,
602
- BinOpKind :: Shr => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
603
- BinOpKind :: Shl => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
604
- BinOpKind :: BitXor => Some ( Constant :: Int ( l ^ r) ) ,
605
- BinOpKind :: BitOr => Some ( Constant :: Int ( l | r) ) ,
606
- BinOpKind :: BitAnd => Some ( Constant :: Int ( l & r) ) ,
607
- BinOpKind :: Eq => Some ( Constant :: Bool ( l == r) ) ,
608
- BinOpKind :: Ne => Some ( Constant :: Bool ( l != r) ) ,
609
- BinOpKind :: Lt => Some ( Constant :: Bool ( l < r) ) ,
610
- BinOpKind :: Le => Some ( Constant :: Bool ( l <= r) ) ,
611
- BinOpKind :: Ge => Some ( Constant :: Bool ( l >= r) ) ,
612
- BinOpKind :: Gt => Some ( Constant :: Bool ( l > r) ) ,
613
- _ => None ,
676
+ ty:: Uint ( ity) => {
677
+ let bits = ity. bits ( ) ;
678
+
679
+ match op. node {
680
+ BinOpKind :: Add => l. checked_add ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
681
+ BinOpKind :: Sub => l. checked_sub ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
682
+ BinOpKind :: Mul => l. checked_mul ( r) . and_then ( |n| ity. ensure_fits ( n) ) . map ( Constant :: Int ) ,
683
+ BinOpKind :: Div => l. checked_div ( r) . map ( Constant :: Int ) ,
684
+ BinOpKind :: Rem => l. checked_rem ( r) . map ( Constant :: Int ) ,
685
+ BinOpKind :: Shr if r < bits => l. checked_shr ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
686
+ BinOpKind :: Shl if r < bits => l. checked_shl ( r. try_into ( ) . ok ( ) ?) . map ( Constant :: Int ) ,
687
+ BinOpKind :: BitXor => Some ( Constant :: Int ( l ^ r) ) ,
688
+ BinOpKind :: BitOr => Some ( Constant :: Int ( l | r) ) ,
689
+ BinOpKind :: BitAnd => Some ( Constant :: Int ( l & r) ) ,
690
+ BinOpKind :: Eq => Some ( Constant :: Bool ( l == r) ) ,
691
+ BinOpKind :: Ne => Some ( Constant :: Bool ( l != r) ) ,
692
+ BinOpKind :: Lt => Some ( Constant :: Bool ( l < r) ) ,
693
+ BinOpKind :: Le => Some ( Constant :: Bool ( l <= r) ) ,
694
+ BinOpKind :: Ge => Some ( Constant :: Bool ( l >= r) ) ,
695
+ BinOpKind :: Gt => Some ( Constant :: Bool ( l > r) ) ,
696
+ _ => None ,
697
+ }
614
698
} ,
615
699
_ => None ,
616
700
} ,
0 commit comments