@@ -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