Skip to content

Commit be38081

Browse files
committed
Auto merge of #77369 - jonas-schievink:validate-storage-liveness, r=wesleywiser
-Zvalidate-mir: Assert that storage is allocated on local use This extends the MIR validator to check that locals are only used when their backing storage is currently allocated via `StorageLive`. The result of this is that miscompilations such as #77359 are caught and turned into ICEs. The PR currently fails tests because miscompilations such as #77359 are caught and turned into ICEs. I have confirmed that tests pass (even with `-Zvalidate-mir`) once `SimplifyArmIdentity` is turned into a no-op (except mir-opt tests, of course).
2 parents a585aae + c47011f commit be38081

File tree

2 files changed

+42
-14
lines changed

2 files changed

+42
-14
lines changed

compiler/rustc_mir/src/transform/validate.rs

+38-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
//! Validates the MIR to ensure that invariants are upheld.
22
3+
use crate::dataflow::impls::MaybeStorageLive;
4+
use crate::dataflow::{Analysis, ResultsCursor};
5+
use crate::util::storage::AlwaysLiveLocals;
6+
37
use super::{MirPass, MirSource};
4-
use rustc_middle::mir::visit::Visitor;
5-
use rustc_middle::{
6-
mir::{
7-
AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
8-
Statement, StatementKind, Terminator, TerminatorKind,
9-
},
10-
ty::{
11-
self,
12-
relate::{Relate, RelateResult, TypeRelation},
13-
ParamEnv, Ty, TyCtxt,
14-
},
8+
use rustc_middle::mir::visit::{PlaceContext, Visitor};
9+
use rustc_middle::mir::{
10+
AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPhase, Operand, Rvalue,
11+
Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfo,
1512
};
13+
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
14+
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
1615

1716
#[derive(Copy, Clone, Debug)]
1817
enum EdgeKind {
@@ -33,9 +32,18 @@ pub struct Validator {
3332

3433
impl<'tcx> MirPass<'tcx> for Validator {
3534
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
36-
let param_env = tcx.param_env(source.def_id());
35+
let def_id = source.def_id();
36+
let param_env = tcx.param_env(def_id);
3737
let mir_phase = self.mir_phase;
38-
TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase }.visit_body(body);
38+
39+
let always_live_locals = AlwaysLiveLocals::new(body);
40+
let storage_liveness = MaybeStorageLive::new(always_live_locals)
41+
.into_engine(tcx, body, def_id)
42+
.iterate_to_fixpoint()
43+
.into_results_cursor(body);
44+
45+
TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase, storage_liveness }
46+
.visit_body(body);
3947
}
4048
}
4149

@@ -138,6 +146,7 @@ struct TypeChecker<'a, 'tcx> {
138146
tcx: TyCtxt<'tcx>,
139147
param_env: ParamEnv<'tcx>,
140148
mir_phase: MirPhase,
149+
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
141150
}
142151

143152
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -210,6 +219,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
210219
}
211220

212221
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
222+
fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
223+
if context.is_use() {
224+
// Uses of locals must occur while the local's storage is allocated.
225+
self.storage_liveness.seek_after_primary_effect(location);
226+
let locals_with_storage = self.storage_liveness.get();
227+
if !locals_with_storage.contains(*local) {
228+
self.fail(location, format!("use of local {:?}, which has no storage here", local));
229+
}
230+
}
231+
}
232+
233+
fn visit_var_debug_info(&mut self, _var_debug_info: &VarDebugInfo<'tcx>) {
234+
// Debuginfo can contain field projections, which count as a use of the base local. Skip
235+
// debuginfo so that we avoid the storage liveness assertion in that case.
236+
}
237+
213238
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
214239
// `Operand::Copy` is only supposed to be used with `Copy` types.
215240
if let Operand::Copy(place) = operand {

src/test/mir-opt/simplify_try_if_let.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
// compile-flags: -Zmir-opt-level=1 -Zunsound-mir-opts
2+
// ignore-test
3+
// FIXME: the pass is unsound and causes ICEs in the MIR validator
4+
25
// EMIT_MIR simplify_try_if_let.{impl#0}-append.SimplifyArmIdentity.diff
36

47
use std::ptr::NonNull;
@@ -19,7 +22,7 @@ impl LinkedList {
1922

2023
pub fn append(&mut self, other: &mut Self) {
2124
match self.tail {
22-
None => { },
25+
None => {}
2326
Some(mut tail) => {
2427
// `as_mut` is okay here because we have exclusive access to the entirety
2528
// of both lists.

0 commit comments

Comments
 (0)