@@ -10,7 +10,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item
1010use rustc_lexer:: tokenize;
1111use rustc_lint:: LateContext ;
1212use 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 } ;
1414use rustc_middle:: { bug, mir, span_bug} ;
1515use rustc_span:: symbol:: { Ident , Symbol } ;
1616use rustc_span:: SyntaxContext ;
@@ -51,6 +51,63 @@ pub enum Constant<'tcx> {
5151 Err ,
5252}
5353
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+
54111impl < ' tcx > PartialEq for Constant < ' tcx > {
55112 fn eq ( & self , other : & Self ) -> bool {
56113 match ( self , other) {
@@ -433,8 +490,15 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
433490 match * o {
434491 Int ( value) => {
435492 let ty:: Int ( ity) = * ty. kind ( ) else { return None } ;
493+ let ( min, _) = ity. min_max ( ) ?;
436494 // sign extend
437495 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+
438502 let value = value. checked_neg ( ) ?;
439503 // clear unused bits
440504 Some ( Int ( unsext ( self . lcx . tcx , value, ity) ) )
@@ -570,17 +634,33 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
570634 match ( l, r) {
571635 ( Constant :: Int ( l) , Some ( Constant :: Int ( r) ) ) => match * self . typeck_results . expr_ty_opt ( left) ?. kind ( ) {
572636 ty:: Int ( ity) => {
637+ let ( ty_min_value, _) = ity. min_max ( ) ?;
638+ let bits = ity. bits ( ) ;
573639 let l = sext ( self . lcx . tcx , l, ity) ;
574640 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+
575651 let zext = |n : i128 | Constant :: Int ( unsext ( self . lcx . tcx , n, ity) ) ;
576652 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) ,
580658 BinOpKind :: Div if r != 0 => l. checked_div ( r) . map ( zext) ,
581659 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) ,
584664 BinOpKind :: BitXor => Some ( zext ( l ^ r) ) ,
585665 BinOpKind :: BitOr => Some ( zext ( l | r) ) ,
586666 BinOpKind :: BitAnd => Some ( zext ( l & r) ) ,
@@ -593,24 +673,28 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
593673 _ => None ,
594674 }
595675 } ,
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+ }
614698 } ,
615699 _ => None ,
616700 } ,
0 commit comments