From ac0a3d106bf33395da6251562800ab8df5e60d3a Mon Sep 17 00:00:00 2001 From: Albin Stjerna Date: Fri, 12 Jul 2019 22:48:02 +0200 Subject: [PATCH] polonius: add generation of liveneness-related facts Notably contains an ugly hack to generate initialization information for variables that will go away when we have that functionality in Polonius. --- src/librustc/mir/mod.rs | 7 ++ src/librustc_mir/borrow_check/flows.rs | 8 +- src/librustc_mir/borrow_check/nll/facts.rs | 9 +- src/librustc_mir/borrow_check/nll/mod.rs | 4 +- .../nll/type_check/liveness/mod.rs | 13 +-- .../nll/type_check/liveness/polonius.rs | 94 +++++++++++++++++++ .../nll/type_check/liveness/trace.rs | 82 +++++++++++++++- 7 files changed, 197 insertions(+), 20 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index ff868bf2a2a..2190f122880 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -9,6 +9,7 @@ use crate::hir::def_id::DefId; 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}; @@ -600,6 +601,12 @@ newtype_index! { } } +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 { diff --git a/src/librustc_mir/borrow_check/flows.rs b/src/librustc_mir/borrow_check/flows.rs index 9a9310fbe05..0b98dbaf58a 100644 --- a/src/librustc_mir/borrow_check/flows.rs +++ b/src/librustc_mir/borrow_check/flows.rs @@ -3,7 +3,7 @@ //! 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; @@ -21,6 +21,8 @@ use either::Either; use std::fmt; use std::rc::Rc; +crate type PoloniusOutput = Output; + // (forced to be `pub` due to its use as an associated type below.) crate struct Flows<'b, 'tcx> { borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>, @@ -28,7 +30,7 @@ crate struct Flows<'b, 'tcx> { pub ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'tcx>>, /// Polonius Output - pub polonius_output: Option>>, + pub polonius_output: Option>, } impl<'b, 'tcx> Flows<'b, 'tcx> { @@ -36,7 +38,7 @@ 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>>, + polonius_output: Option>, ) -> Self { Flows { borrows, diff --git a/src/librustc_mir/borrow_check/nll/facts.rs b/src/librustc_mir/borrow_check/nll/facts.rs index d84afeac185..33854d991ee 100644 --- a/src/librustc_mir/borrow_check/nll/facts.rs +++ b/src/librustc_mir/borrow_check/nll/facts.rs @@ -2,6 +2,7 @@ use crate::borrow_check::location::{LocationIndex, LocationTable}; 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; @@ -10,7 +11,7 @@ use std::fs::{self, File}; use std::io::Write; use std::path::Path; -crate type AllFacts = PoloniusAllFacts; +crate type AllFacts = PoloniusAllFacts; crate trait AllFactsExt { /// Returns `true` if there is a need to gather `AllFacts` given the @@ -60,6 +61,12 @@ impl AllFactsExt for AllFacts { outlives, region_live_at, invalidates, + var_used, + var_defined, + var_drop_used, + var_uses_region, + var_drops_region, + var_initialized_on_exit, ]) } Ok(()) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index eb63e0de195..a2c5ad202e8 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -11,7 +11,7 @@ use crate::transform::MirSource; 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; @@ -84,7 +84,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( errors_buffer: &mut Vec, ) -> ( RegionInferenceContext<'tcx>, - Option>>, + Option>>, Option>, ) { let mut all_facts = if AllFacts::enabled(infcx.tcx) { diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs index 4af78fa5e0f..8633341c321 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs @@ -15,6 +15,7 @@ use std::rc::Rc; use super::TypeChecker; mod local_use_map; +mod polonius; mod trace; /// Combines liveness analysis with initialization analysis to @@ -57,15 +58,9 @@ pub(super) fn generate<'tcx>( }; 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); } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs new file mode 100644 index 00000000000..20d7ec55e3e --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs @@ -0,0 +1,94 @@ +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)); + }; + }); +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs index f160f658f55..dc3b7bdfe30 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs @@ -1,13 +1,14 @@ 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; @@ -130,6 +131,12 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> { 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); @@ -150,6 +157,63 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> { } } + // 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(); @@ -211,6 +275,11 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> { 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); @@ -487,6 +556,8 @@ impl LivenessContext<'_, '_, '_, 'tcx> { live_at, self.location_table, ); + + polonius::add_var_drops_regions(&mut self.typeck, dropped_local, &kind); } } @@ -505,14 +576,15 @@ impl LivenessContext<'_, '_, '_, 'tcx> { 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);