Skip to content

Commit cb272d5

Browse files
committed
Auto merge of #72093 - jonas-schievink:unmut, r=oli-obk
Avoid `Operand::Copy` with `&mut T` This is generally unsound to do, as the copied type is assumed to implement `Copy`. Closes #46420
2 parents 9eedd13 + fe1753a commit cb272d5

File tree

8 files changed

+116
-11
lines changed

8 files changed

+116
-11
lines changed

src/librustc_interface/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ fn test_debugging_options_tracking_hash() {
511511
untracked!(ui_testing, true);
512512
untracked!(unpretty, Some("expanded".to_string()));
513513
untracked!(unstable_options, true);
514+
untracked!(validate_mir, true);
514515
untracked!(verbose, true);
515516

516517
macro_rules! tracked {

src/librustc_mir/shim.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ fn build_call_shim<'tcx>(
700700

701701
let rcvr = rcvr_adjustment.map(|rcvr_adjustment| match rcvr_adjustment {
702702
Adjustment::Identity => Operand::Move(rcvr_place()),
703-
Adjustment::Deref => Operand::Copy(tcx.mk_place_deref(rcvr_place())),
703+
Adjustment::Deref => Operand::Move(tcx.mk_place_deref(rcvr_place())), // Can't copy `&mut`
704704
Adjustment::DerefMove => Operand::Move(tcx.mk_place_deref(rcvr_place())),
705705
Adjustment::RefMut => {
706706
// let rcvr = &mut rcvr;

src/librustc_mir/transform/instcombine.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! Performs various peephole optimizations.
22
33
use crate::transform::{MirPass, MirSource};
4-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4+
use rustc_data_structures::fx::FxHashMap;
55
use rustc_index::vec::Idx;
66
use rustc_middle::mir::visit::{MutVisitor, Visitor};
77
use rustc_middle::mir::{
8-
Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue,
8+
Body, Constant, Local, Location, Mutability, Operand, Place, PlaceRef, ProjectionElem, Rvalue,
99
};
1010
use rustc_middle::ty::{self, TyCtxt};
1111
use std::mem;
@@ -39,7 +39,7 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
3939
}
4040

4141
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
42-
if self.optimizations.and_stars.remove(&location) {
42+
if let Some(mtbl) = self.optimizations.and_stars.remove(&location) {
4343
debug!("replacing `&*`: {:?}", rvalue);
4444
let new_place = match rvalue {
4545
Rvalue::Ref(_, _, place) => {
@@ -57,7 +57,10 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
5757
}
5858
_ => bug!("Detected `&*` but didn't find `&*`!"),
5959
};
60-
*rvalue = Rvalue::Use(Operand::Copy(new_place))
60+
*rvalue = Rvalue::Use(match mtbl {
61+
Mutability::Mut => Operand::Move(new_place),
62+
Mutability::Not => Operand::Copy(new_place),
63+
});
6164
}
6265

6366
if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) {
@@ -88,8 +91,10 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
8891
if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } =
8992
place.as_ref()
9093
{
91-
if Place::ty_from(local, proj_base, self.body, self.tcx).ty.is_region_ptr() {
92-
self.optimizations.and_stars.insert(location);
94+
// The dereferenced place must have type `&_`.
95+
let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty;
96+
if let ty::Ref(_, _, mtbl) = ty.kind {
97+
self.optimizations.and_stars.insert(location, mtbl);
9398
}
9499
}
95100
}
@@ -109,6 +114,6 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
109114

110115
#[derive(Default)]
111116
struct OptimizationList<'tcx> {
112-
and_stars: FxHashSet<Location>,
117+
and_stars: FxHashMap<Location, Mutability>,
113118
arrays_lengths: FxHashMap<Location, Constant<'tcx>>,
114119
}

src/librustc_mir/transform/mod.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub mod simplify_branches;
3939
pub mod simplify_try;
4040
pub mod uninhabited_enum_branching;
4141
pub mod unreachable_prop;
42+
pub mod validate;
4243

4344
pub(crate) fn provide(providers: &mut Providers<'_>) {
4445
self::check_unsafety::provide(providers);
@@ -147,12 +148,18 @@ pub fn run_passes(
147148
passes: &[&[&dyn MirPass<'tcx>]],
148149
) {
149150
let phase_index = mir_phase.phase_index();
151+
let source = MirSource { instance, promoted };
152+
let validate = tcx.sess.opts.debugging_opts.validate_mir;
150153

151154
if body.phase >= mir_phase {
152155
return;
153156
}
154157

155-
let source = MirSource { instance, promoted };
158+
if validate {
159+
validate::Validator { when: format!("input to phase {:?}", mir_phase) }
160+
.run_pass(tcx, source, body);
161+
}
162+
156163
let mut index = 0;
157164
let mut run_pass = |pass: &dyn MirPass<'tcx>| {
158165
let run_hooks = |body: &_, index, is_after| {
@@ -169,6 +176,11 @@ pub fn run_passes(
169176
pass.run_pass(tcx, source, body);
170177
run_hooks(body, index, true);
171178

179+
if validate {
180+
validate::Validator { when: format!("after {} in phase {:?}", pass.name(), mir_phase) }
181+
.run_pass(tcx, source, body);
182+
}
183+
172184
index += 1;
173185
};
174186

@@ -179,6 +191,11 @@ pub fn run_passes(
179191
}
180192

181193
body.phase = mir_phase;
194+
195+
if mir_phase == MirPhase::Optimized {
196+
validate::Validator { when: format!("end of phase {:?}", mir_phase) }
197+
.run_pass(tcx, source, body);
198+
}
182199
}
183200

184201
fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! Validates the MIR to ensure that invariants are upheld.
2+
3+
use super::{MirPass, MirSource};
4+
use rustc_middle::mir::visit::Visitor;
5+
use rustc_middle::{
6+
mir::{Body, Location, Operand, Rvalue, Statement, StatementKind},
7+
ty::{ParamEnv, TyCtxt},
8+
};
9+
use rustc_span::{def_id::DefId, Span, DUMMY_SP};
10+
11+
pub struct Validator {
12+
/// Describes at which point in the pipeline this validation is happening.
13+
pub when: String,
14+
}
15+
16+
impl<'tcx> MirPass<'tcx> for Validator {
17+
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
18+
let def_id = source.def_id();
19+
let param_env = tcx.param_env(def_id);
20+
TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
21+
}
22+
}
23+
24+
struct TypeChecker<'a, 'tcx> {
25+
when: &'a str,
26+
def_id: DefId,
27+
body: &'a Body<'tcx>,
28+
tcx: TyCtxt<'tcx>,
29+
param_env: ParamEnv<'tcx>,
30+
}
31+
32+
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
33+
fn fail(&self, span: Span, msg: impl AsRef<str>) {
34+
// We use `delay_span_bug` as we might see broken MIR when other errors have already
35+
// occurred.
36+
self.tcx.sess.diagnostic().delay_span_bug(
37+
span,
38+
&format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
39+
);
40+
}
41+
}
42+
43+
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
44+
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
45+
// `Operand::Copy` is only supposed to be used with `Copy` types.
46+
if let Operand::Copy(place) = operand {
47+
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
48+
49+
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) {
50+
self.fail(
51+
DUMMY_SP,
52+
format!("`Operand::Copy` with non-`Copy` type {} at {:?}", ty, location),
53+
);
54+
}
55+
}
56+
57+
self.super_operand(operand, location);
58+
}
59+
60+
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
61+
// The sides of an assignment must not alias. Currently this just checks whether the places
62+
// are identical.
63+
if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
64+
match rvalue {
65+
Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
66+
if dest == src {
67+
self.fail(
68+
DUMMY_SP,
69+
format!(
70+
"encountered `Assign` statement with overlapping memory at {:?}",
71+
location
72+
),
73+
);
74+
}
75+
}
76+
_ => {}
77+
}
78+
}
79+
}
80+
}

src/librustc_session/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
10451045
"adds unstable command line options to rustc interface (default: no)"),
10461046
use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED],
10471047
"use legacy .ctors section for initializers rather than .init_array"),
1048+
validate_mir: bool = (false, parse_bool, [UNTRACKED],
1049+
"validate MIR after each transformation"),
10481050
verbose: bool = (false, parse_bool, [UNTRACKED],
10491051
"in general, enable more debug printouts (default: no)"),
10501052
verify_llvm_ir: bool = (false, parse_bool, [TRACKED],

src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn a(_1: &mut [T]) -> &mut [T] {
1515
StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
1616
StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
1717
_4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
18-
_3 = _4; // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL
18+
_3 = move _4; // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL
1919
_2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
2020
StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:14: 3:15
2121
_0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15

src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn b(_1: &mut std::boxed::Box<T>) -> &mut T {
1818
_4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
1919
StorageLive(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL
2020
_5 = &mut (*(*_4)); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL
21-
_3 = _5; // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL
21+
_3 = move _5; // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL
2222
StorageDead(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL
2323
_2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
2424
StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:14: 8:15

0 commit comments

Comments
 (0)