use crate::hir::{self, InlineAsm as HirInlineAsm};
use crate::mir::interpret::{ConstValue, InterpError, Scalar};
use crate::mir::visit::MirVisitable;
+use polonius_engine::Atom;
use rustc_data_structures::bit_set::BitMatrix;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
}
}
+impl Atom for Local {
+ fn index(self) -> usize {
+ Idx::index(self)
+ }
+}
+
/// Classifies locals into categories. See `Body::local_kind`.
#[derive(PartialEq, Eq, Debug, HashStable)]
pub enum LocalKind {
//! FIXME: this might be better as a "generic" fixed-point combinator,
//! but is not as ugly as it is right now.
-use rustc::mir::{BasicBlock, Location};
+use rustc::mir::{BasicBlock, Local, Location};
use rustc::ty::RegionVid;
use rustc_data_structures::bit_set::BitIter;
use std::fmt;
use std::rc::Rc;
+crate type PoloniusOutput = Output<RegionVid, BorrowIndex, LocationIndex, Local>;
+
// (forced to be `pub` due to its use as an associated type below.)
crate struct Flows<'b, 'tcx> {
borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>,
pub ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'tcx>>,
/// Polonius Output
- pub polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
+ pub polonius_output: Option<Rc<PoloniusOutput>>,
}
impl<'b, 'tcx> Flows<'b, 'tcx> {
borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>,
uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'tcx>>,
ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'tcx>>,
- polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
+ polonius_output: Option<Rc<PoloniusOutput>>,
) -> Self {
Flows {
borrows,
use crate::dataflow::indexes::BorrowIndex;
use polonius_engine::AllFacts as PoloniusAllFacts;
use polonius_engine::Atom;
+use rustc::mir::Local;
use rustc::ty::{RegionVid, TyCtxt};
use rustc_data_structures::indexed_vec::Idx;
use std::error::Error;
use std::io::Write;
use std::path::Path;
-crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex>;
+crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex, Local>;
crate trait AllFactsExt {
/// Returns `true` if there is a need to gather `AllFacts` given the
outlives,
region_live_at,
invalidates,
+ var_used,
+ var_defined,
+ var_drop_used,
+ var_uses_region,
+ var_drops_region,
+ var_initialized_on_exit,
])
}
Ok(())
use crate::borrow_check::Upvar;
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
-use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Body};
+use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Local, Body};
use rustc::ty::{self, RegionKind, RegionVid};
use rustc_errors::Diagnostic;
use std::fmt::Debug;
errors_buffer: &mut Vec<Diagnostic>,
) -> (
RegionInferenceContext<'tcx>,
- Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
+ Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex, Local>>>,
Option<ClosureRegionRequirements<'tcx>>,
) {
let mut all_facts = if AllFacts::enabled(infcx.tcx) {
use super::TypeChecker;
mod local_use_map;
+mod polonius;
mod trace;
/// Combines liveness analysis with initialization analysis to
};
if !live_locals.is_empty() {
- trace::trace(
- typeck,
- body,
- elements,
- flow_inits,
- move_data,
- live_locals,
- location_table,
- );
+ trace::trace(typeck, body, elements, flow_inits, move_data, live_locals, location_table);
+
+ polonius::populate_var_liveness_facts(typeck, body, location_table);
}
}
--- /dev/null
+use crate::borrow_check::location::{LocationIndex, LocationTable};
+use crate::util::liveness::{categorize, DefUse};
+use rustc::mir::visit::{PlaceContext, Visitor};
+use rustc::mir::{Body, Local, Location};
+use rustc::ty::subst::Kind;
+use rustc::ty::Ty;
+
+use super::TypeChecker;
+
+type VarPointRelations = Vec<(Local, LocationIndex)>;
+
+struct LivenessPointFactsExtractor<'me> {
+ var_defined: &'me mut VarPointRelations,
+ var_used: &'me mut VarPointRelations,
+ location_table: &'me LocationTable,
+}
+
+// A Visitor to walk through the MIR and extract point-wise facts
+impl LivenessPointFactsExtractor<'_> {
+ fn location_to_index(&self, location: Location) -> LocationIndex {
+ self.location_table.mid_index(location)
+ }
+
+ fn insert_def(&mut self, local: Local, location: Location) {
+ debug!("LivenessFactsExtractor::insert_def()");
+ self.var_defined.push((local, self.location_to_index(location)));
+ }
+
+ fn insert_use(&mut self, local: Local, location: Location) {
+ debug!("LivenessFactsExtractor::insert_use()");
+ self.var_used.push((local, self.location_to_index(location)));
+ }
+}
+
+impl Visitor<'tcx> for LivenessPointFactsExtractor<'_> {
+ fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) {
+ match categorize(context) {
+ Some(DefUse::Def) => self.insert_def(local, location),
+ Some(DefUse::Use) => self.insert_use(local, location),
+ _ => (),
+ // NOTE: Drop handling is now done in trace()
+ }
+ }
+}
+
+fn add_var_uses_regions(typeck: &mut TypeChecker<'_, 'tcx>, local: Local, ty: Ty<'tcx>) {
+ debug!("add_regions(local={:?}, type={:?})", local, ty);
+ typeck.tcx().for_each_free_region(&ty, |region| {
+ let region_vid = typeck.borrowck_context.universal_regions.to_region_vid(region);
+ debug!("add_regions for region {:?}", region_vid);
+ if let Some(facts) = typeck.borrowck_context.all_facts {
+ facts.var_uses_region.push((local, region_vid));
+ }
+ });
+}
+
+pub(super) fn populate_var_liveness_facts(
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ mir: &Body<'tcx>,
+ location_table: &LocationTable,
+) {
+ debug!("populate_var_liveness_facts()");
+
+ if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
+ LivenessPointFactsExtractor {
+ var_defined: &mut facts.var_defined,
+ var_used: &mut facts.var_used,
+ location_table,
+ }
+ .visit_body(mir);
+ }
+
+ for (local, local_decl) in mir.local_decls.iter_enumerated() {
+ add_var_uses_regions(typeck, local, local_decl.ty);
+ }
+}
+
+// For every potentially drop()-touched region `region` in `local`'s type
+// (`kind`), emit a Polonius `var_drops_region(local, region)` fact.
+pub(super) fn add_var_drops_regions(
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ local: Local,
+ kind: &Kind<'tcx>,
+) {
+ debug!("add_var_drops_region(local={:?}, kind={:?}", local, kind);
+ let tcx = typeck.tcx();
+
+ tcx.for_each_free_region(kind, |drop_live_region| {
+ let region_vid = typeck.borrowck_context.universal_regions.to_region_vid(drop_live_region);
+ if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
+ facts.var_drops_region.push((local, region_vid));
+ };
+ });
+}
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
use crate::borrow_check::nll::type_check::liveness::local_use_map::LocalUseMap;
+use crate::borrow_check::nll::type_check::liveness::polonius;
use crate::borrow_check::nll::type_check::NormalizeLocation;
use crate::borrow_check::nll::type_check::TypeChecker;
use crate::dataflow::indexes::MovePathIndex;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces};
use rustc::infer::canonical::QueryRegionConstraints;
-use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, Body};
+use rustc::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
use rustc::traits::query::type_op::outlives::DropckOutlives;
use rustc::traits::query::type_op::TypeOp;
for local in live_locals {
self.reset_local_state();
self.add_defs_for(local);
+
+ // FIXME: this is temporary until we can generate our own initialization
+ if self.cx.typeck.borrowck_context.all_facts.is_some() {
+ self.add_polonius_var_initialized_on_exit_for(local)
+ }
+
self.compute_use_live_points_for(local);
self.compute_drop_live_points_for(local);
}
}
+ // WARNING: panics if self.cx.typeck.borrowck_context.all_facts != None
+ //
+ // FIXME: this analysis (the initialization tracking) should be
+ // done in Polonius, but isn't yet.
+ fn add_polonius_var_initialized_on_exit_for(&mut self, local: Local) {
+ let move_path = self.cx.move_data.rev_lookup.find_local(local);
+ let facts = self.cx.typeck.borrowck_context.all_facts.as_mut().unwrap();
+ for block in self.cx.body.basic_blocks().indices() {
+ debug!("polonius: generating initialization facts for {:?} in {:?}", local, block);
+
+ // iterate through the block, applying the effects of each statement
+ // up to and including location, and populate `var_initialized_on_exit`
+ self.cx.flow_inits.reset_to_entry_of(block);
+ let start_location = Location { block, statement_index: 0 };
+ self.cx.flow_inits.apply_local_effect(start_location);
+
+ for statement_index in 0..self.cx.body[block].statements.len() {
+ let current_location = Location { block, statement_index };
+
+ self.cx.flow_inits.reconstruct_statement_effect(current_location);
+
+ // statement has not yet taken effect:
+ if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
+ facts
+ .var_initialized_on_exit
+ .push((local, self.cx.location_table.start_index(current_location)));
+ }
+
+ // statement has now taken effect
+ self.cx.flow_inits.apply_local_effect(current_location);
+
+ if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
+ facts
+ .var_initialized_on_exit
+ .push((local, self.cx.location_table.mid_index(current_location)));
+ }
+ }
+
+ let terminator_location = self.cx.body.terminator_loc(block);
+
+ if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
+ facts
+ .var_initialized_on_exit
+ .push((local, self.cx.location_table.start_index(terminator_location)));
+ }
+
+ // apply the effects of the terminator and push it if needed
+ self.cx.flow_inits.reset_to_exit_of(block);
+
+ if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
+ facts
+ .var_initialized_on_exit
+ .push((local, self.cx.location_table.mid_index(terminator_location)));
+ }
+ }
+ }
+
/// Clear the value of fields that are "per local variable".
fn reset_local_state(&mut self) {
self.defs.clear();
debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);
if self.cx.initialized_at_terminator(location.block, mpi) {
+ // FIXME: this analysis (the initialization tracking) should be
+ // done in Polonius, but isn't yet.
+ if let Some(facts) = self.cx.typeck.borrowck_context.all_facts {
+ facts.var_drop_used.push((local, self.cx.location_table.mid_index(location)));
+ }
if self.drop_live_at.insert(drop_point) {
self.drop_locations.push(location);
self.stack.push(drop_point);
live_at,
self.location_table,
);
+
+ polonius::add_var_drops_regions(&mut self.typeck, dropped_local, &kind);
}
}
let tcx = typeck.tcx();
tcx.for_each_free_region(&value, |live_region| {
- let live_region_vid = typeck.borrowck_context
- .universal_regions
- .to_region_vid(live_region);
- typeck.borrowck_context
+ let live_region_vid =
+ typeck.borrowck_context.universal_regions.to_region_vid(live_region);
+ typeck
+ .borrowck_context
.constraints
.liveness_constraints
.add_elements(live_region_vid, live_at);
+ // FIXME: remove this when we can generate our own region-live-at reliably
if let Some(facts) = typeck.borrowck_context.all_facts {
for point in live_at.iter() {
let loc = elements.to_location(point);