Skip to content

Commit 9916c09

Browse files
authored
Rollup 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 3e98225 + 3b47280 commit 9916c09

File tree

200 files changed

+1280
-995
lines changed

Some content is hidden

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

200 files changed

+1280
-995
lines changed

src/librustc_codegen_ssa/mir/block.rs

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

src/librustc_middle/mir/mod.rs

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,10 +1244,10 @@ pub enum TerminatorKind<'tcx> {
12441244
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
12451245
pub enum AssertKind<O> {
12461246
BoundsCheck { len: O, index: O },
1247-
Overflow(BinOp),
1248-
OverflowNeg,
1249-
DivisionByZero,
1250-
RemainderByZero,
1247+
Overflow(BinOp, O, O),
1248+
OverflowNeg(O),
1249+
DivisionByZero(O),
1250+
RemainderByZero(O),
12511251
ResumedAfterReturn(GeneratorKind),
12521252
ResumedAfterPanic(GeneratorKind),
12531253
}
@@ -1520,17 +1520,17 @@ impl<O> AssertKind<O> {
15201520
pub fn description(&self) -> &'static str {
15211521
use AssertKind::*;
15221522
match self {
1523-
Overflow(BinOp::Add) => "attempt to add with overflow",
1524-
Overflow(BinOp::Sub) => "attempt to subtract with overflow",
1525-
Overflow(BinOp::Mul) => "attempt to multiply with overflow",
1526-
Overflow(BinOp::Div) => "attempt to divide with overflow",
1527-
Overflow(BinOp::Rem) => "attempt to calculate the remainder with overflow",
1528-
OverflowNeg => "attempt to negate with overflow",
1529-
Overflow(BinOp::Shr) => "attempt to shift right with overflow",
1530-
Overflow(BinOp::Shl) => "attempt to shift left with overflow",
1531-
Overflow(op) => bug!("{:?} cannot overflow", op),
1532-
DivisionByZero => "attempt to divide by zero",
1533-
RemainderByZero => "attempt to calculate the remainder with a divisor of zero",
1523+
Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
1524+
Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
1525+
Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
1526+
Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
1527+
Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
1528+
OverflowNeg(_) => "attempt to negate with overflow",
1529+
Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
1530+
Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
1531+
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
1532+
DivisionByZero(_) => "attempt to divide by zero",
1533+
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
15341534
ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
15351535
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
15361536
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
@@ -1544,12 +1544,54 @@ impl<O> AssertKind<O> {
15441544
where
15451545
O: Debug,
15461546
{
1547+
use AssertKind::*;
15471548
match self {
1548-
AssertKind::BoundsCheck { ref len, ref index } => write!(
1549+
BoundsCheck { ref len, ref index } => write!(
15491550
f,
15501551
"\"index out of bounds: the len is {{}} but the index is {{}}\", {:?}, {:?}",
15511552
len, index
15521553
),
1554+
1555+
OverflowNeg(op) => {
1556+
write!(f, "\"attempt to negate {{}} which would overflow\", {:?}", op)
1557+
}
1558+
DivisionByZero(op) => write!(f, "\"attempt to divide {{}} by zero\", {:?}", op),
1559+
RemainderByZero(op) => write!(
1560+
f,
1561+
"\"attempt to calculate the remainder of {{}} with a divisor of zero\", {:?}",
1562+
op
1563+
),
1564+
Overflow(BinOp::Add, l, r) => write!(
1565+
f,
1566+
"\"attempt to compute `{{}} + {{}}` which would overflow\", {:?}, {:?}",
1567+
l, r
1568+
),
1569+
Overflow(BinOp::Sub, l, r) => write!(
1570+
f,
1571+
"\"attempt to compute `{{}} - {{}}` which would overflow\", {:?}, {:?}",
1572+
l, r
1573+
),
1574+
Overflow(BinOp::Mul, l, r) => write!(
1575+
f,
1576+
"\"attempt to compute `{{}} * {{}}` which would overflow\", {:?}, {:?}",
1577+
l, r
1578+
),
1579+
Overflow(BinOp::Div, l, r) => write!(
1580+
f,
1581+
"\"attempt to compute `{{}} / {{}}` which would overflow\", {:?}, {:?}",
1582+
l, r
1583+
),
1584+
Overflow(BinOp::Rem, l, r) => write!(
1585+
f,
1586+
"\"attempt to compute the remainder of `{{}} % {{}}` which would overflow\", {:?}, {:?}",
1587+
l, r
1588+
),
1589+
Overflow(BinOp::Shr, _, r) => {
1590+
write!(f, "\"attempt to shift right by {{}} which would overflow\", {:?}", r)
1591+
}
1592+
Overflow(BinOp::Shl, _, r) => {
1593+
write!(f, "\"attempt to shift left by {{}} which would overflow\", {:?}", r)
1594+
}
15531595
_ => write!(f, "\"{}\"", self.description()),
15541596
}
15551597
}
@@ -1562,6 +1604,34 @@ impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
15621604
BoundsCheck { ref len, ref index } => {
15631605
write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index)
15641606
}
1607+
OverflowNeg(op) => write!(f, "attempt to negate {:#?} which would overflow", op),
1608+
DivisionByZero(op) => write!(f, "attempt to divide {:#?} by zero", op),
1609+
RemainderByZero(op) => {
1610+
write!(f, "attempt to calculate the remainder of {:#?} with a divisor of zero", op)
1611+
}
1612+
Overflow(BinOp::Add, l, r) => {
1613+
write!(f, "attempt to compute `{:#?} + {:#?}` which would overflow", l, r)
1614+
}
1615+
Overflow(BinOp::Sub, l, r) => {
1616+
write!(f, "attempt to compute `{:#?} - {:#?}` which would overflow", l, r)
1617+
}
1618+
Overflow(BinOp::Mul, l, r) => {
1619+
write!(f, "attempt to compute `{:#?} * {:#?}` which would overflow", l, r)
1620+
}
1621+
Overflow(BinOp::Div, l, r) => {
1622+
write!(f, "attempt to compute `{:#?} / {:#?}` which would overflow", l, r)
1623+
}
1624+
Overflow(BinOp::Rem, l, r) => write!(
1625+
f,
1626+
"attempt to compute the remainder of `{:#?} % {:#?}` which would overflow",
1627+
l, r
1628+
),
1629+
Overflow(BinOp::Shr, _, r) => {
1630+
write!(f, "attempt to shift right by {:#?} which would overflow", r)
1631+
}
1632+
Overflow(BinOp::Shl, _, r) => {
1633+
write!(f, "attempt to shift left by {:#?} which would overflow", r)
1634+
}
15651635
_ => write!(f, "{}", self.description()),
15661636
}
15671637
}

src/librustc_middle/mir/type_foldable.rs

Lines changed: 11 additions & 13 deletions
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

Lines changed: 7 additions & 1 deletion
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

Lines changed: 111 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 7 additions & 30 deletions
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() => {

src/librustc_mir/const_eval/error.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::error::Error;
22
use std::fmt;
33

44
use rustc_middle::mir::AssertKind;
5+
use rustc_middle::ty::ConstInt;
56
use rustc_span::{Span, Symbol};
67

78
use super::InterpCx;
@@ -13,7 +14,7 @@ pub enum ConstEvalErrKind {
1314
NeedsRfc(String),
1415
ConstAccessesStatic,
1516
ModifiedGlobal,
16-
AssertFailure(AssertKind<u64>),
17+
AssertFailure(AssertKind<ConstInt>),
1718
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
1819
}
1920

0 commit comments

Comments
 (0)