validate: storage must be allocated on local use
This commit is contained in:
parent
4dedf5edd5
commit
c1a431edc3
1 changed files with 29 additions and 3 deletions
|
@ -1,7 +1,12 @@
|
||||||
//! Validates the MIR to ensure that invariants are upheld.
|
//! Validates the MIR to ensure that invariants are upheld.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
dataflow::impls::MaybeStorageLive, dataflow::Analysis, dataflow::ResultsCursor,
|
||||||
|
util::storage::AlwaysLiveLocals,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{MirPass, MirSource};
|
use super::{MirPass, MirSource};
|
||||||
use rustc_middle::mir::visit::Visitor;
|
use rustc_middle::mir::{visit::PlaceContext, visit::Visitor, Local};
|
||||||
use rustc_middle::{
|
use rustc_middle::{
|
||||||
mir::{
|
mir::{
|
||||||
AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
|
AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
|
||||||
|
@ -33,9 +38,18 @@ pub struct Validator {
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for Validator {
|
impl<'tcx> MirPass<'tcx> for Validator {
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||||
let param_env = tcx.param_env(source.def_id());
|
let def_id = source.def_id();
|
||||||
|
let param_env = tcx.param_env(def_id);
|
||||||
let mir_phase = self.mir_phase;
|
let mir_phase = self.mir_phase;
|
||||||
TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase }.visit_body(body);
|
|
||||||
|
let always_live_locals = AlwaysLiveLocals::new(body);
|
||||||
|
let storage_liveness = MaybeStorageLive::new(always_live_locals)
|
||||||
|
.into_engine(tcx, body, def_id)
|
||||||
|
.iterate_to_fixpoint()
|
||||||
|
.into_results_cursor(body);
|
||||||
|
|
||||||
|
TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase, storage_liveness }
|
||||||
|
.visit_body(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +152,7 @@ struct TypeChecker<'a, 'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
param_env: ParamEnv<'tcx>,
|
param_env: ParamEnv<'tcx>,
|
||||||
mir_phase: MirPhase,
|
mir_phase: MirPhase,
|
||||||
|
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
|
@ -210,6 +225,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
|
fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
|
||||||
|
if context.is_use() {
|
||||||
|
// Uses of locals must occur while the local's storage is allocated.
|
||||||
|
self.storage_liveness.seek_after_primary_effect(location);
|
||||||
|
let locals_with_storage = self.storage_liveness.get();
|
||||||
|
if !locals_with_storage.contains(*local) {
|
||||||
|
self.fail(location, format!("use of local {:?}, which has no storage here", local));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||||
// `Operand::Copy` is only supposed to be used with `Copy` types.
|
// `Operand::Copy` is only supposed to be used with `Copy` types.
|
||||||
if let Operand::Copy(place) = operand {
|
if let Operand::Copy(place) = operand {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue