Skip to content

Commit 4707702

Browse files
authored
Rollup merge of rust-lang#64890 - wesleywiser:const_prop_rvalue, r=oli-obk
[const-prop] Handle remaining MIR Rvalue cases r? @oli-obk
2 parents e5bef70 + 5804c3b commit 4707702

18 files changed

+297
-93
lines changed

src/librustc_mir/transform/const_prop.rs

+78-86
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc::hir::def::DefKind;
88
use rustc::hir::def_id::DefId;
99
use rustc::mir::{
1010
AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue,
11-
Local, NullOp, UnOp, StatementKind, Statement, LocalKind,
11+
Local, UnOp, StatementKind, Statement, LocalKind,
1212
TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp,
1313
SourceScope, SourceScopeLocalData, LocalDecl, BasicBlock,
1414
};
@@ -118,7 +118,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
118118
struct ConstPropMachine;
119119

120120
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
121-
type MemoryKinds= !;
121+
type MemoryKinds = !;
122122
type PointerTag = ();
123123
type ExtraFnVal = !;
124124

@@ -459,97 +459,81 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
459459
) -> Option<Const<'tcx>> {
460460
let span = source_info.span;
461461

462-
// if this isn't a supported operation, then return None
463-
match rvalue {
464-
Rvalue::Repeat(..) |
465-
Rvalue::Aggregate(..) |
466-
Rvalue::NullaryOp(NullOp::Box, _) |
467-
Rvalue::Discriminant(..) => return None,
468-
469-
Rvalue::Use(_) |
470-
Rvalue::Len(_) |
471-
Rvalue::Cast(..) |
472-
Rvalue::NullaryOp(..) |
473-
Rvalue::CheckedBinaryOp(..) |
474-
Rvalue::Ref(..) |
475-
Rvalue::UnaryOp(..) |
476-
Rvalue::BinaryOp(..) => { }
477-
}
478-
479462
// perform any special checking for specific Rvalue types
480-
if let Rvalue::UnaryOp(op, arg) = rvalue {
481-
trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg);
482-
let overflow_check = self.tcx.sess.overflow_checks();
483-
484-
self.use_ecx(source_info, |this| {
485-
// We check overflow in debug mode already
486-
// so should only check in release mode.
487-
if *op == UnOp::Neg && !overflow_check {
488-
let ty = arg.ty(&this.local_decls, this.tcx);
489-
490-
if ty.is_integral() {
491-
let arg = this.ecx.eval_operand(arg, None)?;
492-
let prim = this.ecx.read_immediate(arg)?;
493-
// Need to do overflow check here: For actual CTFE, MIR
494-
// generation emits code that does this before calling the op.
495-
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
496-
throw_panic!(OverflowNeg)
463+
match rvalue {
464+
Rvalue::UnaryOp(UnOp::Neg, arg) => {
465+
trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
466+
let overflow_check = self.tcx.sess.overflow_checks();
467+
468+
self.use_ecx(source_info, |this| {
469+
// We check overflow in debug mode already
470+
// so should only check in release mode.
471+
if !overflow_check {
472+
let ty = arg.ty(&this.local_decls, this.tcx);
473+
474+
if ty.is_integral() {
475+
let arg = this.ecx.eval_operand(arg, None)?;
476+
let prim = this.ecx.read_immediate(arg)?;
477+
// Need to do overflow check here: For actual CTFE, MIR
478+
// generation emits code that does this before calling the op.
479+
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
480+
throw_panic!(OverflowNeg)
481+
}
497482
}
498483
}
499-
}
500484

501-
Ok(())
502-
})?;
503-
} else if let Rvalue::BinaryOp(op, left, right) = rvalue {
504-
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
505-
506-
let r = self.use_ecx(source_info, |this| {
507-
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
508-
})?;
509-
if *op == BinOp::Shr || *op == BinOp::Shl {
510-
let left_bits = place_layout.size.bits();
511-
let right_size = r.layout.size;
512-
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
513-
if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
514-
let source_scope_local_data = match self.source_scope_local_data {
515-
ClearCrossCrate::Set(ref data) => data,
516-
ClearCrossCrate::Clear => return None,
517-
};
518-
let dir = if *op == BinOp::Shr {
519-
"right"
520-
} else {
521-
"left"
522-
};
523-
let hir_id = source_scope_local_data[source_info.scope].lint_root;
524-
self.tcx.lint_hir(
525-
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
526-
hir_id,
527-
span,
528-
&format!("attempt to shift {} with overflow", dir));
529-
return None;
530-
}
485+
Ok(())
486+
})?;
531487
}
532-
self.use_ecx(source_info, |this| {
533-
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
534-
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
535-
536-
// We check overflow in debug mode already
537-
// so should only check in release mode.
538-
if !this.tcx.sess.overflow_checks() && overflow {
539-
let err = err_panic!(Overflow(*op)).into();
540-
return Err(err);
488+
489+
Rvalue::BinaryOp(op, left, right) => {
490+
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
491+
492+
let r = self.use_ecx(source_info, |this| {
493+
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
494+
})?;
495+
if *op == BinOp::Shr || *op == BinOp::Shl {
496+
let left_bits = place_layout.size.bits();
497+
let right_size = r.layout.size;
498+
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
499+
if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
500+
let source_scope_local_data = match self.source_scope_local_data {
501+
ClearCrossCrate::Set(ref data) => data,
502+
ClearCrossCrate::Clear => return None,
503+
};
504+
let dir = if *op == BinOp::Shr {
505+
"right"
506+
} else {
507+
"left"
508+
};
509+
let hir_id = source_scope_local_data[source_info.scope].lint_root;
510+
self.tcx.lint_hir(
511+
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
512+
hir_id,
513+
span,
514+
&format!("attempt to shift {} with overflow", dir));
515+
return None;
516+
}
541517
}
518+
self.use_ecx(source_info, |this| {
519+
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
520+
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
521+
522+
// We check overflow in debug mode already
523+
// so should only check in release mode.
524+
if !this.tcx.sess.overflow_checks() && overflow {
525+
let err = err_panic!(Overflow(*op)).into();
526+
return Err(err);
527+
}
542528

543-
Ok(())
544-
})?;
545-
} else if let Rvalue::Ref(_, _, place) = rvalue {
546-
trace!("checking Ref({:?})", place);
547-
// FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
548-
// from a function argument that hasn't been assigned to in this function.
549-
if let Place {
550-
base: PlaceBase::Local(local),
551-
projection: box []
552-
} = place {
529+
Ok(())
530+
})?;
531+
}
532+
533+
Rvalue::Ref(_, _, Place { base: PlaceBase::Local(local), projection: box [] }) => {
534+
trace!("checking Ref({:?})", place);
535+
// FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
536+
// from a function argument that hasn't been assigned to in this function.
553537
let alive =
554538
if let LocalValue::Live(_) = self.ecx.frame().locals[*local].value {
555539
true
@@ -560,6 +544,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
560544
return None;
561545
}
562546
}
547+
548+
Rvalue::Aggregate(_, operands) if operands.len() == 0 => {
549+
// FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that
550+
// `SimplifyLocals` doesn't know it can remove.
551+
return None;
552+
}
553+
554+
_ => { }
563555
}
564556

565557
self.use_ecx(source_info, |this| {

src/test/compile-fail/consts/const-err3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ fn main() {
1414
//~^ ERROR const_err
1515
let _e = [5u8][1];
1616
//~^ ERROR const_err
17+
//~| ERROR this expression will panic at runtime
1718
black_box(b);
1819
black_box(c);
1920
black_box(d);
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// compile-flags: -O
2+
3+
fn main() {
4+
let x = (0, 1, 2).1 + 0;
5+
}
6+
7+
// END RUST SOURCE
8+
// START rustc.main.ConstProp.before.mir
9+
// bb0: {
10+
// ...
11+
// _3 = (const 0i32, const 1i32, const 2i32);
12+
// _2 = (_3.1: i32);
13+
// _1 = Add(move _2, const 0i32);
14+
// ...
15+
// }
16+
// END rustc.main.ConstProp.before.mir
17+
// START rustc.main.ConstProp.after.mir
18+
// bb0: {
19+
// ...
20+
// _3 = (const 0i32, const 1i32, const 2i32);
21+
// _2 = const 1i32;
22+
// _1 = Add(move _2, const 0i32);
23+
// ...
24+
// }
25+
// END rustc.main.ConstProp.after.mir

src/test/mir-opt/const_prop/boxes.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// compile-flags: -O
2+
3+
#![feature(box_syntax)]
4+
5+
// Note: this test verifies that we, in fact, do not const prop `box`
6+
7+
fn main() {
8+
let x = *(box 42) + 0;
9+
}
10+
11+
// END RUST SOURCE
12+
// START rustc.main.ConstProp.before.mir
13+
// bb0: {
14+
// ...
15+
// _4 = Box(i32);
16+
// (*_4) = const 42i32;
17+
// _3 = move _4;
18+
// ...
19+
// _2 = (*_3);
20+
// _1 = Add(move _2, const 0i32);
21+
// ...
22+
// drop(_3) -> [return: bb2, unwind: bb1];
23+
// }
24+
// bb1 (cleanup): {
25+
// resume;
26+
// }
27+
// bb2: {
28+
// ...
29+
// _0 = ();
30+
// ...
31+
// }
32+
// END rustc.main.ConstProp.before.mir
33+
// START rustc.main.ConstProp.after.mir
34+
// bb0: {
35+
// ...
36+
// _4 = Box(i32);
37+
// (*_4) = const 42i32;
38+
// _3 = move _4;
39+
// ...
40+
// _2 = (*_3);
41+
// _1 = Add(move _2, const 0i32);
42+
// ...
43+
// drop(_3) -> [return: bb2, unwind: bb1];
44+
// }
45+
// bb1 (cleanup): {
46+
// resume;
47+
// }
48+
// bb2: {
49+
// ...
50+
// _0 = ();
51+
// ...
52+
// }
53+
// END rustc.main.ConstProp.after.mir
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// compile-flags: -O
2+
3+
fn main() {
4+
let x = (if let Some(true) = Some(true) { 42 } else { 10 }) + 0;
5+
}
6+
7+
// END RUST SOURCE
8+
// START rustc.main.ConstProp.before.mir
9+
// bb0: {
10+
// ...
11+
// _3 = std::option::Option::<bool>::Some(const true,);
12+
// _4 = discriminant(_3);
13+
// switchInt(move _4) -> [1isize: bb3, otherwise: bb2];
14+
// }
15+
// bb1: {
16+
// _2 = const 42i32;
17+
// goto -> bb4;
18+
// }
19+
// bb2: {
20+
// _2 = const 10i32;
21+
// goto -> bb4;
22+
// }
23+
// bb3: {
24+
// switchInt(((_3 as Some).0: bool)) -> [false: bb2, otherwise: bb1];
25+
// }
26+
// bb4: {
27+
// _1 = Add(move _2, const 0i32);
28+
// ...
29+
// }
30+
// END rustc.main.ConstProp.before.mir
31+
// START rustc.main.ConstProp.after.mir
32+
// bb0: {
33+
// ...
34+
// _3 = const Scalar(0x01) : std::option::Option<bool>;
35+
// _4 = const 1isize;
36+
// switchInt(const 1isize) -> [1isize: bb3, otherwise: bb2];
37+
// }
38+
// bb1: {
39+
// _2 = const 42i32;
40+
// goto -> bb4;
41+
// }
42+
// bb2: {
43+
// _2 = const 10i32;
44+
// goto -> bb4;
45+
// }
46+
// bb3: {
47+
// switchInt(const true) -> [false: bb2, otherwise: bb1];
48+
// }
49+
// bb4: {
50+
// _1 = Add(move _2, const 0i32);
51+
// ...
52+
// }
53+
// END rustc.main.ConstProp.after.mir

src/test/mir-opt/const_prop/repeat.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// compile-flags: -O
2+
3+
fn main() {
4+
let x: u32 = [42; 8][2] + 0;
5+
}
6+
7+
// END RUST SOURCE
8+
// START rustc.main.ConstProp.before.mir
9+
// bb0: {
10+
// ...
11+
// _3 = [const 42u32; 8];
12+
// ...
13+
// _4 = const 2usize;
14+
// _5 = const 8usize;
15+
// _6 = Lt(_4, _5);
16+
// assert(move _6, "index out of bounds: the len is move _5 but the index is _4") -> bb1;
17+
// }
18+
// bb1: {
19+
// _2 = _3[_4];
20+
// _1 = Add(move _2, const 0u32);
21+
// ...
22+
// return;
23+
// }
24+
// END rustc.main.ConstProp.before.mir
25+
// START rustc.main.ConstProp.after.mir
26+
// bb0: {
27+
// ...
28+
// _6 = const true;
29+
// assert(const true, "index out of bounds: the len is move _5 but the index is _4") -> bb1;
30+
// }
31+
// bb1: {
32+
// _2 = const 42u32;
33+
// _1 = Add(move _2, const 0u32);
34+
// ...
35+
// return;
36+
// }
37+
// END rustc.main.ConstProp.after.mir

src/test/run-fail/overflowing-rsh-5.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// compile-flags: -C debug-assertions
33

44
#![warn(exceeding_bitshifts)]
5+
#![warn(const_err)]
56

67
fn main() {
78
let _n = 1i64 >> [64][0];

src/test/run-fail/overflowing-rsh-6.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// compile-flags: -C debug-assertions
33

44
#![warn(exceeding_bitshifts)]
5+
#![warn(const_err)]
56
#![feature(const_indexing)]
67

78
fn main() {

0 commit comments

Comments
 (0)