Skip to content

Commit c1a431e

Browse files
validate: storage must be allocated on local use
1 parent 4dedf5e commit c1a431e

File tree

1 file changed

+29
-3
lines changed

1 file changed

+29
-3
lines changed

compiler/rustc_mir/src/transform/validate.rs

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
//! Validates the MIR to ensure that invariants are upheld.
22
3+
use crate::{
4+
dataflow::impls::MaybeStorageLive, dataflow::Analysis, dataflow::ResultsCursor,
5+
util::storage::AlwaysLiveLocals,
6+
};
7+
38
use super::{MirPass, MirSource};
4-
use rustc_middle::mir::visit::Visitor;
9+
use rustc_middle::mir::{visit::PlaceContext, visit::Visitor, Local};
510
use rustc_middle::{
611
mir::{
712
AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
@@ -33,9 +38,18 @@ pub struct Validator {
3338

3439
impl<'tcx> MirPass<'tcx> for Validator {
3540
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
36-
let param_env = tcx.param_env(source.def_id());
41+
let def_id = source.def_id();
42+
let param_env = tcx.param_env(def_id);
3743
let mir_phase = self.mir_phase;
38-
TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase }.visit_body(body);
44+
45+
let always_live_locals = AlwaysLiveLocals::new(body);
46+
let storage_liveness = MaybeStorageLive::new(always_live_locals)
47+
.into_engine(tcx, body, def_id)
48+
.iterate_to_fixpoint()
49+
.into_results_cursor(body);
50+
51+
TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase, storage_liveness }
52+
.visit_body(body);
3953
}
4054
}
4155

@@ -138,6 +152,7 @@ struct TypeChecker<'a, 'tcx> {
138152
tcx: TyCtxt<'tcx>,
139153
param_env: ParamEnv<'tcx>,
140154
mir_phase: MirPhase,
155+
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
141156
}
142157

143158
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -210,6 +225,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
210225
}
211226

212227
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
228+
fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
229+
if context.is_use() {
230+
// Uses of locals must occur while the local's storage is allocated.
231+
self.storage_liveness.seek_after_primary_effect(location);
232+
let locals_with_storage = self.storage_liveness.get();
233+
if !locals_with_storage.contains(*local) {
234+
self.fail(location, format!("use of local {:?}, which has no storage here", local));
235+
}
236+
}
237+
}
238+
213239
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
214240
// `Operand::Copy` is only supposed to be used with `Copy` types.
215241
if let Operand::Copy(place) = operand {

0 commit comments

Comments
 (0)