Skip to content

Commit 7750c3d

Browse files
committed
Auto merge of rust-lang#73513 - oli-obk:const_binop_overflow, r=estebank
Show the values and computation that would overflow a const evaluation or propagation Fixes rust-lang#71134 In contrast to the example in the issue it doesn't use individual spans for each operand. The effort required to implement that is quite high compared to the little (if at all) benefit it would bring to diagnostics. cc @shepmaster The way this is implemented it is also fairly easy to do the same for overflow panics at runtime, but that should be done in a separate PR since it may have runtime performance implications.
2 parents 9672b5e + 819cde5 commit 7750c3d

File tree

202 files changed

+1273
-986
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

202 files changed

+1273
-986
lines changed

src/librustc_codegen_ssa/mir/block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
380380
// checked operation, just a comparison with the minimum
381381
// value, so we have to check for the assert message.
382382
if !bx.check_overflow() {
383-
if let AssertKind::OverflowNeg = *msg {
383+
if let AssertKind::OverflowNeg(_) = *msg {
384384
const_cond = Some(expected);
385385
}
386386
}

src/librustc_middle/mir/mod.rs

+88-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//!
33
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
44
5+
// ignore-tidy-filelength
6+
57
use crate::mir::interpret::{GlobalAlloc, Scalar};
68
use crate::mir::visit::MirVisitable;
79
use crate::ty::adjustment::PointerCast;
@@ -1246,10 +1248,10 @@ pub enum TerminatorKind<'tcx> {
12461248
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
12471249
pub enum AssertKind<O> {
12481250
BoundsCheck { len: O, index: O },
1249-
Overflow(BinOp),
1250-
OverflowNeg,
1251-
DivisionByZero,
1252-
RemainderByZero,
1251+
Overflow(BinOp, O, O),
1252+
OverflowNeg(O),
1253+
DivisionByZero(O),
1254+
RemainderByZero(O),
12531255
ResumedAfterReturn(GeneratorKind),
12541256
ResumedAfterPanic(GeneratorKind),
12551257
}
@@ -1522,17 +1524,17 @@ impl<O> AssertKind<O> {
15221524
pub fn description(&self) -> &'static str {
15231525
use AssertKind::*;
15241526
match self {
1525-
Overflow(BinOp::Add) => "attempt to add with overflow",
1526-
Overflow(BinOp::Sub) => "attempt to subtract with overflow",
1527-
Overflow(BinOp::Mul) => "attempt to multiply with overflow",
1528-
Overflow(BinOp::Div) => "attempt to divide with overflow",
1529-
Overflow(BinOp::Rem) => "attempt to calculate the remainder with overflow",
1530-
OverflowNeg => "attempt to negate with overflow",
1531-
Overflow(BinOp::Shr) => "attempt to shift right with overflow",
1532-
Overflow(BinOp::Shl) => "attempt to shift left with overflow",
1533-
Overflow(op) => bug!("{:?} cannot overflow", op),
1534-
DivisionByZero => "attempt to divide by zero",
1535-
RemainderByZero => "attempt to calculate the remainder with a divisor of zero",
1527+
Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
1528+
Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
1529+
Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
1530+
Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
1531+
Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
1532+
OverflowNeg(_) => "attempt to negate with overflow",
1533+
Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
1534+
Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
1535+
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
1536+
DivisionByZero(_) => "attempt to divide by zero",
1537+
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
15361538
ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
15371539
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
15381540
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
@@ -1546,12 +1548,54 @@ impl<O> AssertKind<O> {
15461548
where
15471549
O: Debug,
15481550
{
1551+
use AssertKind::*;
15491552
match self {
1550-
AssertKind::BoundsCheck { ref len, ref index } => write!(
1553+
BoundsCheck { ref len, ref index } => write!(
15511554
f,
15521555
"\"index out of bounds: the len is {{}} but the index is {{}}\", {:?}, {:?}",
15531556
len, index
15541557
),
1558+
1559+
OverflowNeg(op) => {
1560+
write!(f, "\"attempt to negate {{}} which would overflow\", {:?}", op)
1561+
}
1562+
DivisionByZero(op) => write!(f, "\"attempt to divide {{}} by zero\", {:?}", op),
1563+
RemainderByZero(op) => write!(
1564+
f,
1565+
"\"attempt to calculate the remainder of {{}} with a divisor of zero\", {:?}",
1566+
op
1567+
),
1568+
Overflow(BinOp::Add, l, r) => write!(
1569+
f,
1570+
"\"attempt to compute `{{}} + {{}}` which would overflow\", {:?}, {:?}",
1571+
l, r
1572+
),
1573+
Overflow(BinOp::Sub, l, r) => write!(
1574+
f,
1575+
"\"attempt to compute `{{}} - {{}}` which would overflow\", {:?}, {:?}",
1576+
l, r
1577+
),
1578+
Overflow(BinOp::Mul, l, r) => write!(
1579+
f,
1580+
"\"attempt to compute `{{}} * {{}}` which would overflow\", {:?}, {:?}",
1581+
l, r
1582+
),
1583+
Overflow(BinOp::Div, l, r) => write!(
1584+
f,
1585+
"\"attempt to compute `{{}} / {{}}` which would overflow\", {:?}, {:?}",
1586+
l, r
1587+
),
1588+
Overflow(BinOp::Rem, l, r) => write!(
1589+
f,
1590+
"\"attempt to compute the remainder of `{{}} % {{}}` which would overflow\", {:?}, {:?}",
1591+
l, r
1592+
),
1593+
Overflow(BinOp::Shr, _, r) => {
1594+
write!(f, "\"attempt to shift right by {{}} which would overflow\", {:?}", r)
1595+
}
1596+
Overflow(BinOp::Shl, _, r) => {
1597+
write!(f, "\"attempt to shift left by {{}} which would overflow\", {:?}", r)
1598+
}
15551599
_ => write!(f, "\"{}\"", self.description()),
15561600
}
15571601
}
@@ -1564,6 +1608,34 @@ impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
15641608
BoundsCheck { ref len, ref index } => {
15651609
write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index)
15661610
}
1611+
OverflowNeg(op) => write!(f, "attempt to negate {:#?} which would overflow", op),
1612+
DivisionByZero(op) => write!(f, "attempt to divide {:#?} by zero", op),
1613+
RemainderByZero(op) => {
1614+
write!(f, "attempt to calculate the remainder of {:#?} with a divisor of zero", op)
1615+
}
1616+
Overflow(BinOp::Add, l, r) => {
1617+
write!(f, "attempt to compute `{:#?} + {:#?}` which would overflow", l, r)
1618+
}
1619+
Overflow(BinOp::Sub, l, r) => {
1620+
write!(f, "attempt to compute `{:#?} - {:#?}` which would overflow", l, r)
1621+
}
1622+
Overflow(BinOp::Mul, l, r) => {
1623+
write!(f, "attempt to compute `{:#?} * {:#?}` which would overflow", l, r)
1624+
}
1625+
Overflow(BinOp::Div, l, r) => {
1626+
write!(f, "attempt to compute `{:#?} / {:#?}` which would overflow", l, r)
1627+
}
1628+
Overflow(BinOp::Rem, l, r) => write!(
1629+
f,
1630+
"attempt to compute the remainder of `{:#?} % {:#?}` which would overflow",
1631+
l, r
1632+
),
1633+
Overflow(BinOp::Shr, _, r) => {
1634+
write!(f, "attempt to shift right by {:#?} which would overflow", r)
1635+
}
1636+
Overflow(BinOp::Shl, _, r) => {
1637+
write!(f, "attempt to shift left by {:#?} which would overflow", r)
1638+
}
15671639
_ => write!(f, "{}", self.description()),
15681640
}
15691641
}

src/librustc_middle/mir/type_foldable.rs

+11-13
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,14 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
5858
Assert { ref cond, expected, ref msg, target, cleanup } => {
5959
use AssertKind::*;
6060
let msg = match msg {
61-
BoundsCheck { ref len, ref index } => {
61+
BoundsCheck { len, index } => {
6262
BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) }
6363
}
64-
Overflow(_)
65-
| OverflowNeg
66-
| DivisionByZero
67-
| RemainderByZero
68-
| ResumedAfterReturn(_)
69-
| ResumedAfterPanic(_) => msg.clone(),
64+
Overflow(op, l, r) => Overflow(*op, l.fold_with(folder), r.fold_with(folder)),
65+
OverflowNeg(op) => OverflowNeg(op.fold_with(folder)),
66+
DivisionByZero(op) => DivisionByZero(op.fold_with(folder)),
67+
RemainderByZero(op) => RemainderByZero(op.fold_with(folder)),
68+
ResumedAfterReturn(_) | ResumedAfterPanic(_) => msg.clone(),
7069
};
7170
Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
7271
}
@@ -117,12 +116,11 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
117116
BoundsCheck { ref len, ref index } => {
118117
len.visit_with(visitor) || index.visit_with(visitor)
119118
}
120-
Overflow(_)
121-
| OverflowNeg
122-
| DivisionByZero
123-
| RemainderByZero
124-
| ResumedAfterReturn(_)
125-
| ResumedAfterPanic(_) => false,
119+
Overflow(_, l, r) => l.visit_with(visitor) || r.visit_with(visitor),
120+
OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
121+
op.visit_with(visitor)
122+
}
123+
ResumedAfterReturn(_) | ResumedAfterPanic(_) => false,
126124
}
127125
} else {
128126
false

src/librustc_middle/mir/visit.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,13 @@ macro_rules! make_mir_visitor {
571571
self.visit_operand(len, location);
572572
self.visit_operand(index, location);
573573
}
574-
Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero |
574+
Overflow(_, l, r) => {
575+
self.visit_operand(l, location);
576+
self.visit_operand(r, location);
577+
}
578+
OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
579+
self.visit_operand(op, location);
580+
}
575581
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
576582
// Nothing to visit
577583
}

src/librustc_middle/ty/consts.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use crate::mir::interpret::truncate;
2+
use rustc_target::abi::Size;
3+
4+
#[derive(Copy, Clone)]
5+
/// A type for representing any integer. Only used for printing.
6+
// FIXME: Use this for the integer-tree representation needed for type level ints and
7+
// const generics?
8+
pub struct ConstInt {
9+
/// Number of bytes of the integer. Only 1, 2, 4, 8, 16 are legal values.
10+
size: u8,
11+
/// Whether the value is of a signed integer type.
12+
signed: bool,
13+
/// Whether the value is a `usize` or `isize` type.
14+
is_ptr_sized_integral: bool,
15+
/// Raw memory of the integer. All bytes beyond the `size` are unused and must be zero.
16+
raw: u128,
17+
}
18+
19+
impl ConstInt {
20+
pub fn new(raw: u128, size: Size, signed: bool, is_ptr_sized_integral: bool) -> Self {
21+
assert!(raw <= truncate(u128::MAX, size));
22+
Self { raw, size: size.bytes() as u8, signed, is_ptr_sized_integral }
23+
}
24+
}
25+
26+
impl std::fmt::Debug for ConstInt {
27+
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28+
let Self { size, signed, raw, is_ptr_sized_integral } = *self;
29+
if signed {
30+
let bit_size = size * 8;
31+
let min = 1u128 << (bit_size - 1);
32+
let max = min - 1;
33+
if raw == min {
34+
match (size, is_ptr_sized_integral) {
35+
(_, true) => write!(fmt, "isize::MIN"),
36+
(1, _) => write!(fmt, "i8::MIN"),
37+
(2, _) => write!(fmt, "i16::MIN"),
38+
(4, _) => write!(fmt, "i32::MIN"),
39+
(8, _) => write!(fmt, "i64::MIN"),
40+
(16, _) => write!(fmt, "i128::MIN"),
41+
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
42+
}
43+
} else if raw == max {
44+
match (size, is_ptr_sized_integral) {
45+
(_, true) => write!(fmt, "isize::MAX"),
46+
(1, _) => write!(fmt, "i8::MAX"),
47+
(2, _) => write!(fmt, "i16::MAX"),
48+
(4, _) => write!(fmt, "i32::MAX"),
49+
(8, _) => write!(fmt, "i64::MAX"),
50+
(16, _) => write!(fmt, "i128::MAX"),
51+
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
52+
}
53+
} else {
54+
match size {
55+
1 => write!(fmt, "{}", raw as i8)?,
56+
2 => write!(fmt, "{}", raw as i16)?,
57+
4 => write!(fmt, "{}", raw as i32)?,
58+
8 => write!(fmt, "{}", raw as i64)?,
59+
16 => write!(fmt, "{}", raw as i128)?,
60+
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
61+
}
62+
if fmt.alternate() {
63+
match (size, is_ptr_sized_integral) {
64+
(_, true) => write!(fmt, "_isize")?,
65+
(1, _) => write!(fmt, "_i8")?,
66+
(2, _) => write!(fmt, "_i16")?,
67+
(4, _) => write!(fmt, "_i32")?,
68+
(8, _) => write!(fmt, "_i64")?,
69+
(16, _) => write!(fmt, "_i128")?,
70+
_ => bug!(),
71+
}
72+
}
73+
Ok(())
74+
}
75+
} else {
76+
let max = truncate(u128::MAX, Size::from_bytes(size));
77+
if raw == max {
78+
match (size, is_ptr_sized_integral) {
79+
(_, true) => write!(fmt, "usize::MAX"),
80+
(1, _) => write!(fmt, "u8::MAX"),
81+
(2, _) => write!(fmt, "u16::MAX"),
82+
(4, _) => write!(fmt, "u32::MAX"),
83+
(8, _) => write!(fmt, "u64::MAX"),
84+
(16, _) => write!(fmt, "u128::MAX"),
85+
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
86+
}
87+
} else {
88+
match size {
89+
1 => write!(fmt, "{}", raw as u8)?,
90+
2 => write!(fmt, "{}", raw as u16)?,
91+
4 => write!(fmt, "{}", raw as u32)?,
92+
8 => write!(fmt, "{}", raw as u64)?,
93+
16 => write!(fmt, "{}", raw as u128)?,
94+
_ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
95+
}
96+
if fmt.alternate() {
97+
match (size, is_ptr_sized_integral) {
98+
(_, true) => write!(fmt, "_usize")?,
99+
(1, _) => write!(fmt, "_u8")?,
100+
(2, _) => write!(fmt, "_u16")?,
101+
(4, _) => write!(fmt, "_u32")?,
102+
(8, _) => write!(fmt, "_u64")?,
103+
(16, _) => write!(fmt, "_u128")?,
104+
_ => bug!(),
105+
}
106+
}
107+
Ok(())
108+
}
109+
}
110+
}
111+
}

src/librustc_middle/ty/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ pub use self::trait_def::TraitDef;
8484

8585
pub use self::query::queries;
8686

87+
pub use self::consts::ConstInt;
88+
8789
pub mod adjustment;
8890
pub mod binding;
8991
pub mod cast;
@@ -108,6 +110,7 @@ pub mod trait_def;
108110
pub mod util;
109111
pub mod walk;
110112

113+
mod consts;
111114
mod context;
112115
mod diagnostics;
113116
mod instance;

src/librustc_middle/ty/print/pretty.rs

+7-30
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
use crate::middle::cstore::{ExternCrate, ExternCrateSource};
2-
use crate::mir::interpret::{
3-
sign_extend, truncate, AllocId, ConstValue, GlobalAlloc, Pointer, Scalar,
4-
};
2+
use crate::mir::interpret::{AllocId, ConstValue, GlobalAlloc, Pointer, Scalar};
53
use crate::ty::layout::IntegerExt;
64
use crate::ty::subst::{GenericArg, GenericArgKind, Subst};
7-
use crate::ty::{self, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable};
5+
use crate::ty::{self, ConstInt, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable};
86
use rustc_apfloat::ieee::{Double, Single};
97
use rustc_apfloat::Float;
108
use rustc_ast::ast;
@@ -981,35 +979,14 @@ pub trait PrettyPrinter<'tcx>:
981979
}
982980
// Int
983981
(Scalar::Raw { data, .. }, ty::Uint(ui)) => {
984-
let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size();
985-
let max = truncate(u128::MAX, bit_size);
986-
987-
let ui_str = ui.name_str();
988-
if data == max {
989-
p!(write("{}::MAX", ui_str))
990-
} else {
991-
if print_ty { p!(write("{}{}", data, ui_str)) } else { p!(write("{}", data)) }
992-
};
982+
let size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size();
983+
let int = ConstInt::new(data, size, false, ty.is_ptr_sized_integral());
984+
if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) }
993985
}
994986
(Scalar::Raw { data, .. }, ty::Int(i)) => {
995987
let size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size();
996-
let bit_size = size.bits() as u128;
997-
let min = 1u128 << (bit_size - 1);
998-
let max = min - 1;
999-
1000-
let i_str = i.name_str();
1001-
match data {
1002-
d if d == min => p!(write("{}::MIN", i_str)),
1003-
d if d == max => p!(write("{}::MAX", i_str)),
1004-
_ => {
1005-
let data = sign_extend(data, size) as i128;
1006-
if print_ty {
1007-
p!(write("{}{}", data, i_str))
1008-
} else {
1009-
p!(write("{}", data))
1010-
}
1011-
}
1012-
}
988+
let int = ConstInt::new(data, size, true, ty.is_ptr_sized_integral());
989+
if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) }
1013990
}
1014991
// Char
1015992
(Scalar::Raw { data, .. }, ty::Char) if char::from_u32(data as u32).is_some() => {

0 commit comments

Comments
 (0)