Skip to content

Commit df116ec

Browse files
committed
Migrate memory overlap check from validator to lint
The check attempts to identify potential undefined behaviour, rather than whether MIR is well-formed. It belongs in the lint not validator.
1 parent a084e06 commit df116ec

File tree

8 files changed

+85
-237
lines changed

8 files changed

+85
-237
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+3-43
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ impl<'tcx> MirPass<'tcx> for Validator {
7474
mir_phase,
7575
unwind_edge_count: 0,
7676
reachable_blocks: traversal::reachable_as_bitset(body),
77-
place_cache: FxHashSet::default(),
7877
value_cache: FxHashSet::default(),
7978
can_unwind,
8079
};
@@ -106,7 +105,6 @@ struct CfgChecker<'a, 'tcx> {
106105
mir_phase: MirPhase,
107106
unwind_edge_count: usize,
108107
reachable_blocks: BitSet<BasicBlock>,
109-
place_cache: FxHashSet<PlaceRef<'tcx>>,
110108
value_cache: FxHashSet<u128>,
111109
// If `false`, then the MIR must not contain `UnwindAction::Continue` or
112110
// `TerminatorKind::Resume`.
@@ -294,19 +292,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
294292

295293
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
296294
match &statement.kind {
297-
StatementKind::Assign(box (dest, rvalue)) => {
298-
// FIXME(JakobDegen): Check this for all rvalues, not just this one.
299-
if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
300-
// The sides of an assignment must not alias. Currently this just checks whether
301-
// the places are identical.
302-
if dest == src {
303-
self.fail(
304-
location,
305-
"encountered `Assign` statement with overlapping memory",
306-
);
307-
}
308-
}
309-
}
310295
StatementKind::AscribeUserType(..) => {
311296
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
312297
self.fail(
@@ -341,7 +326,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
341326
self.fail(location, format!("explicit `{kind:?}` is forbidden"));
342327
}
343328
}
344-
StatementKind::StorageLive(_)
329+
StatementKind::Assign(..)
330+
| StatementKind::StorageLive(_)
345331
| StatementKind::StorageDead(_)
346332
| StatementKind::Intrinsic(_)
347333
| StatementKind::Coverage(_)
@@ -404,10 +390,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
404390
}
405391

406392
// The call destination place and Operand::Move place used as an argument might be
407-
// passed by a reference to the callee. Consequently they must be non-overlapping
408-
// and cannot be packed. Currently this simply checks for duplicate places.
409-
self.place_cache.clear();
410-
self.place_cache.insert(destination.as_ref());
393+
// passed by a reference to the callee. Consequently they cannot be packed.
411394
if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() {
412395
// This is bad! The callee will expect the memory to be aligned.
413396
self.fail(
@@ -418,10 +401,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
418401
),
419402
);
420403
}
421-
let mut has_duplicates = false;
422404
for arg in args {
423405
if let Operand::Move(place) = arg {
424-
has_duplicates |= !self.place_cache.insert(place.as_ref());
425406
if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
426407
// This is bad! The callee will expect the memory to be aligned.
427408
self.fail(
@@ -434,16 +415,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
434415
}
435416
}
436417
}
437-
438-
if has_duplicates {
439-
self.fail(
440-
location,
441-
format!(
442-
"encountered overlapping memory in `Move` arguments to `Call` terminator: {:?}",
443-
terminator.kind,
444-
),
445-
);
446-
}
447418
}
448419
TerminatorKind::Assert { target, unwind, .. } => {
449420
self.check_edge(location, *target, EdgeKind::Normal);
@@ -1112,17 +1083,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11121083
)
11131084
}
11141085
}
1115-
// FIXME(JakobDegen): Check this for all rvalues, not just this one.
1116-
if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
1117-
// The sides of an assignment must not alias. Currently this just checks whether
1118-
// the places are identical.
1119-
if dest == src {
1120-
self.fail(
1121-
location,
1122-
"encountered `Assign` statement with overlapping memory",
1123-
);
1124-
}
1125-
}
11261086
}
11271087
StatementKind::AscribeUserType(..) => {
11281088
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {

compiler/rustc_mir_transform/src/dest_prop.rs

-7
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@
133133
134134
use std::collections::hash_map::{Entry, OccupiedEntry};
135135

136-
use crate::simplify::remove_dead_blocks;
137136
use crate::MirPass;
138137
use rustc_data_structures::fx::FxHashMap;
139138
use rustc_index::bit_set::BitSet;
@@ -241,12 +240,6 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
241240
apply_merges(body, tcx, &merges, &merged_locals);
242241
}
243242

244-
if round_count != 0 {
245-
// Merging can introduce overlap between moved arguments and/or call destination in an
246-
// unreachable code, which validator considers to be ill-formed.
247-
remove_dead_blocks(body);
248-
}
249-
250243
trace!(round_count);
251244
}
252245
}

compiler/rustc_mir_transform/src/lint.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! This pass statically detects code which has undefined behaviour or is likely to be erroneous.
22
//! It can be used to locate problems in MIR building or optimizations. It assumes that all code
33
//! can be executed, so it has false positives.
4+
use rustc_data_structures::fx::FxHashSet;
45
use rustc_index::bit_set::BitSet;
56
use rustc_middle::mir::visit::{PlaceContext, Visitor};
67
use rustc_middle::mir::*;
@@ -31,6 +32,7 @@ pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) {
3132
always_live_locals,
3233
maybe_storage_live,
3334
maybe_storage_dead,
35+
places: Default::default(),
3436
};
3537
for (bb, data) in traversal::reachable(body) {
3638
lint.visit_basic_block_data(bb, data);
@@ -45,6 +47,7 @@ struct Lint<'a, 'tcx> {
4547
always_live_locals: &'a BitSet<Local>,
4648
maybe_storage_live: ResultsCursor<'a, 'tcx, MaybeStorageLive<'a>>,
4749
maybe_storage_dead: ResultsCursor<'a, 'tcx, MaybeStorageDead<'a>>,
50+
places: FxHashSet<PlaceRef<'tcx>>,
4851
}
4952

5053
impl<'a, 'tcx> Lint<'a, 'tcx> {
@@ -75,10 +78,22 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
7578
}
7679

7780
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
78-
match statement.kind {
81+
match &statement.kind {
82+
StatementKind::Assign(box (dest, rvalue)) => {
83+
if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
84+
// The sides of an assignment must not alias. Currently this just checks whether
85+
// the places are identical.
86+
if dest == src {
87+
self.fail(
88+
location,
89+
"encountered `Assign` statement with overlapping memory",
90+
);
91+
}
92+
}
93+
}
7994
StatementKind::StorageLive(local) => {
8095
self.maybe_storage_live.seek_before_primary_effect(location);
81-
if self.maybe_storage_live.get().contains(local) {
96+
if self.maybe_storage_live.get().contains(*local) {
8297
self.fail(
8398
location,
8499
format!("StorageLive({local:?}) which already has storage here"),
@@ -92,7 +107,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
92107
}
93108

94109
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
95-
match terminator.kind {
110+
match &terminator.kind {
96111
TerminatorKind::Return => {
97112
if self.is_fn_like {
98113
self.maybe_storage_live.seek_after_primary_effect(location);
@@ -108,6 +123,28 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
108123
}
109124
}
110125
}
126+
TerminatorKind::Call { args, destination, .. } => {
127+
// The call destination place and Operand::Move place used as an argument might be
128+
// passed by a reference to the callee. Consequently they must be non-overlapping.
129+
// Currently this simply checks for duplicate places.
130+
self.places.clear();
131+
self.places.insert(destination.as_ref());
132+
let mut has_duplicates = false;
133+
for arg in args {
134+
if let Operand::Move(place) = arg {
135+
has_duplicates |= !self.places.insert(place.as_ref());
136+
}
137+
}
138+
if has_duplicates {
139+
self.fail(
140+
location,
141+
format!(
142+
"encountered overlapping memory in `Move` arguments to `Call` terminator: {:?}",
143+
terminator.kind,
144+
),
145+
);
146+
}
147+
}
111148
_ => {}
112149
}
113150

tests/mir-opt/dest-prop/unreachable.f.DestinationPropagation.panic-abort.diff

-82
This file was deleted.

tests/mir-opt/dest-prop/unreachable.f.DestinationPropagation.panic-unwind.diff

-82
This file was deleted.

tests/mir-opt/dest-prop/unreachable.rs

-20
This file was deleted.

0 commit comments

Comments
 (0)