]> git.lizzy.rs Git - rust.git/commitdiff
polonius: add generation of liveneness-related facts
authorAlbin Stjerna <albin.stjerna@gmail.com>
Fri, 12 Jul 2019 20:48:02 +0000 (22:48 +0200)
committerAlbin Stjerna <albin.stjerna@gmail.com>
Fri, 12 Jul 2019 20:48:02 +0000 (22:48 +0200)
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
src/librustc_mir/borrow_check/flows.rs
src/librustc_mir/borrow_check/nll/facts.rs
src/librustc_mir/borrow_check/nll/mod.rs
src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs
src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs

index ff868bf2a2ad2437fbef13967c35822f76e12fe3..2190f122880732128d92051e47b5f1f199ece243 100644 (file)
@@ -9,6 +9,7 @@
 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 @@ pub struct Local {
     }
 }
 
+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 {
index 9a9310fbe05f514ee03f91388a71175584352f2f..0b98dbaf58a807f2e4c7dd7e7f3c65374bb6a8fc 100644 (file)
@@ -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 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>>,
@@ -28,7 +30,7 @@
     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> {
@@ -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<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
+        polonius_output: Option<Rc<PoloniusOutput>>,
     ) -> Self {
         Flows {
             borrows,
index d84afeac1852352e71500112c7e849c00322d300..33854d991eea7b09e90bf6cac2ef3aa81dad2ba2 100644 (file)
@@ -2,6 +2,7 @@
 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::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
@@ -60,6 +61,12 @@ macro_rules! write_facts_to_path {
                 outlives,
                 region_live_at,
                 invalidates,
+                var_used,
+                var_defined,
+                var_drop_used,
+                var_uses_region,
+                var_drops_region,
+                var_initialized_on_exit,
             ])
         }
         Ok(())
index eb63e0de195e5d6692d56ad9e94d9edb4514ab3e..a2c5ad202e8c9d96404ae9240c271a34f52091e7 100644 (file)
@@ -11,7 +11,7 @@
 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<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) {
index 4af78fa5e0f42e4dd64979434aeb0384ff7ebb94..8633341c32182423afcd5585a3cacce766677c5c 100644 (file)
@@ -15,6 +15,7 @@
 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 (file)
index 0000000..20d7ec5
--- /dev/null
@@ -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));
+        };
+    });
+}
index f160f658f557640e68731c9b211cf29e19e6025d..dc3b7bdfe30399caa09b4f4d660fcce776375f2c 100644 (file)
@@ -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 @@ fn compute_for_all_locals(&mut self, live_locals: Vec<Local>) {
         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 @@ fn compute_for_all_locals(&mut self, live_locals: Vec<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();
@@ -211,6 +275,11 @@ fn compute_drop_live_points_for(&mut self, local: Local) {
             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 @@ fn add_drop_live_facts_for(
                 live_at,
                 self.location_table,
             );
+
+            polonius::add_var_drops_regions(&mut self.typeck, dropped_local, &kind);
         }
     }
 
@@ -505,14 +576,15 @@ fn make_all_regions_live(
 
         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);