]> git.lizzy.rs Git - rust.git/commitdiff
add `-Znll-facts` switch that dumps facts for new analysis
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 1 May 2018 14:49:11 +0000 (10:49 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Sat, 5 May 2018 10:03:25 +0000 (06:03 -0400)
src/librustc/session/config.rs
src/librustc_mir/borrow_check/location.rs
src/librustc_mir/borrow_check/nll/constraint_generation.rs
src/librustc_mir/borrow_check/nll/facts.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/mod.rs
src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
src/librustc_mir/lib.rs

index dc97c941567059cd5912250a945af3fdc4f0e938..0beda679e69592794be9d46f30235eaaf668f551 100644 (file)
@@ -1250,6 +1250,8 @@ fn parse_lto(slot: &mut Lto, v: Option<&str>) -> bool {
         "choose which RELRO level to use"),
     nll_subminimal_causes: bool = (false, parse_bool, [UNTRACKED],
         "when tracking region error causes, accept subminimal results for faster execution."),
+    nll_facts: bool = (false, parse_bool, [UNTRACKED],
+                       "dump facts from NLL analysis into side files"),
     disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED],
         "disable user provided type assertion in NLL"),
     trans_time_graph: bool = (false, parse_bool, [UNTRACKED],
index e09f0ee9e2a20b273e33ee4ce0ebdcfa88c2a56b..1284a5b0e2a89a526b1a0399bf930b659bb4a146 100644 (file)
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(dead_code)] // TODO -- will be used in a later commit, remove then
-
 use rustc::mir::{BasicBlock, Location, Mir};
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
index afaedecdf0abe307400847586aebaf1c0a98a838..d34e9434fbf2a0352a5cc4ec36b796f5cf1436f0 100644 (file)
@@ -8,28 +8,37 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use borrow_check::borrow_set::BorrowSet;
+use borrow_check::location::LocationTable;
+use borrow_check::nll::facts::AllFacts;
 use rustc::hir;
-use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue};
+use rustc::infer::InferCtxt;
+use rustc::mir::visit::TyContext;
 use rustc::mir::visit::Visitor;
 use rustc::mir::Place::Projection;
-use rustc::mir::{Local, PlaceProjection, ProjectionElem};
-use rustc::mir::visit::TyContext;
-use rustc::infer::InferCtxt;
-use rustc::ty::{self, CanonicalTy, ClosureSubsts};
-use rustc::ty::subst::Substs;
+use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue};
+use rustc::mir::{Local, PlaceProjection, ProjectionElem, Statement, Terminator};
 use rustc::ty::fold::TypeFoldable;
+use rustc::ty::subst::Substs;
+use rustc::ty::{self, CanonicalTy, ClosureSubsts};
 
+use super::region_infer::{Cause, RegionInferenceContext};
 use super::ToRegionVid;
-use super::region_infer::{RegionInferenceContext, Cause};
 
 pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
     regioncx: &mut RegionInferenceContext<'tcx>,
+    all_facts: &mut Option<AllFacts>,
+    location_table: &LocationTable,
     mir: &Mir<'tcx>,
+    borrow_set: &BorrowSet<'tcx>,
 ) {
     let mut cg = ConstraintGeneration {
+        borrow_set,
         infcx,
         regioncx,
+        location_table,
+        all_facts,
         mir,
     };
 
@@ -41,8 +50,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
 /// 'cg = the duration of the constraint generation process itself.
 struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
     infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
+    all_facts: &'cg mut Option<AllFacts>,
+    location_table: &'cg LocationTable,
     regioncx: &'cg mut RegionInferenceContext<'tcx>,
     mir: &'cg Mir<'tcx>,
+    borrow_set: &'cg BorrowSet<'tcx>,
 }
 
 impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
@@ -68,12 +80,14 @@ fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) {
     /// call. Make them live at the location where they appear.
     fn visit_ty(&mut self, ty: &ty::Ty<'tcx>, ty_context: TyContext) {
         match ty_context {
-            TyContext::ReturnTy(source_info) |
-            TyContext::YieldTy(source_info) |
-            TyContext::LocalDecl { source_info, .. } => {
-                span_bug!(source_info.span,
-                          "should not be visiting outside of the CFG: {:?}",
-                          ty_context);
+            TyContext::ReturnTy(source_info)
+            | TyContext::YieldTy(source_info)
+            | TyContext::LocalDecl { source_info, .. } => {
+                span_bug!(
+                    source_info.span,
+                    "should not be visiting outside of the CFG: {:?}",
+                    ty_context
+                );
             }
             TyContext::Location(location) => {
                 self.add_regular_live_constraint(*ty, location, Cause::LiveOther(location));
@@ -90,25 +104,117 @@ fn visit_closure_substs(&mut self, substs: &ClosureSubsts<'tcx>, location: Locat
         self.super_closure_substs(substs);
     }
 
+    fn visit_statement(
+        &mut self,
+        block: BasicBlock,
+        statement: &Statement<'tcx>,
+        location: Location,
+    ) {
+        if let Some(all_facts) = self.all_facts {
+            all_facts.cfg_edge.push((
+                self.location_table.start_index(location),
+                self.location_table.mid_index(location),
+            ));
+
+            all_facts.cfg_edge.push((
+                self.location_table.mid_index(location),
+                self.location_table
+                    .start_index(location.successor_within_block()),
+            ));
+        }
+
+        self.super_statement(block, statement, location);
+    }
+
+    fn visit_assign(
+        &mut self,
+        block: BasicBlock,
+        place: &Place<'tcx>,
+        rvalue: &Rvalue<'tcx>,
+        location: Location,
+    ) {
+        // When we see `X = ...`, then kill borrows of
+        // `(*X).foo` and so forth.
+        if let Some(all_facts) = self.all_facts {
+            if let Place::Local(temp) = place {
+                if let Some(borrow_indices) = self.borrow_set.local_map.get(temp) {
+                    for &borrow_index in borrow_indices {
+                        let location_index = self.location_table.mid_index(location);
+                        all_facts.killed.push((borrow_index, location_index));
+                    }
+                }
+            }
+        }
+
+        self.super_assign(block, place, rvalue, location);
+    }
+
+    fn visit_terminator(
+        &mut self,
+        block: BasicBlock,
+        terminator: &Terminator<'tcx>,
+        location: Location,
+    ) {
+        if let Some(all_facts) = self.all_facts {
+            all_facts.cfg_edge.push((
+                self.location_table.start_index(location),
+                self.location_table.mid_index(location),
+            ));
+
+            for successor_block in terminator.successors() {
+                all_facts.cfg_edge.push((
+                    self.location_table.mid_index(location),
+                    self.location_table
+                        .start_index(successor_block.start_location()),
+                ));
+            }
+        }
+
+        self.super_terminator(block, terminator, location);
+    }
+
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
 
-        // Look for an rvalue like:
-        //
-        //     & L
-        //
-        // where L is the path that is borrowed. In that case, we have
-        // to add the reborrow constraints (which don't fall out
-        // naturally from the type-checker).
-        if let Rvalue::Ref(region, _bk, ref borrowed_lv) = *rvalue {
-            self.add_reborrow_constraint(location, region, borrowed_lv);
+        match rvalue {
+            Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
+                // In some cases, e.g. when borrowing from an unsafe
+                // place, we don't bother to create a loan, since
+                // there are no conditions to validate.
+                if let Some(all_facts) = self.all_facts {
+                    if let Some(borrow_index) = self.borrow_set.location_map.get(&location) {
+                        let region_vid = region.to_region_vid();
+                        all_facts.borrow_region.push((
+                            region_vid,
+                            *borrow_index,
+                            self.location_table.mid_index(location),
+                        ));
+                    }
+                }
+
+                // Look for an rvalue like:
+                //
+                //     & L
+                //
+                // where L is the path that is borrowed. In that case, we have
+                // to add the reborrow constraints (which don't fall out
+                // naturally from the type-checker).
+                self.add_reborrow_constraint(location, region, borrowed_place);
+            }
+
+            _ => { }
         }
 
         self.super_rvalue(rvalue, location);
     }
 
-    fn visit_user_assert_ty(&mut self, _c_ty: &CanonicalTy<'tcx>,
-                            _local: &Local, _location: Location) { }
+    fn visit_user_assert_ty(
+        &mut self,
+        _c_ty: &CanonicalTy<'tcx>,
+        _local: &Local,
+        _location: Location,
+    ) {
+    }
 }
 
 impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
@@ -122,8 +228,7 @@ fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location, cau
     {
         debug!(
             "add_regular_live_constraint(live_ty={:?}, location={:?})",
-            live_ty,
-            location
+            live_ty, location
         );
 
         self.infcx
@@ -144,8 +249,10 @@ fn add_reborrow_constraint(
     ) {
         let mut borrowed_place = borrowed_place;
 
-        debug!("add_reborrow_constraint({:?}, {:?}, {:?})",
-               location, borrow_region, borrowed_place);
+        debug!(
+            "add_reborrow_constraint({:?}, {:?}, {:?})",
+            location, borrow_region, borrowed_place
+        );
         while let Projection(box PlaceProjection { base, elem }) = borrowed_place {
             debug!("add_reborrow_constraint - iteration {:?}", borrowed_place);
 
@@ -165,12 +272,20 @@ fn add_reborrow_constraint(
                                 location.successor_within_block(),
                             );
 
+                            if let Some(all_facts) = self.all_facts {
+                                all_facts.outlives.push((
+                                    ref_region.to_region_vid(),
+                                    borrow_region.to_region_vid(),
+                                    self.location_table.mid_index(location),
+                                ));
+                            }
+
                             match mutbl {
                                 hir::Mutability::MutImmutable => {
                                     // Immutable reference. We don't need the base
                                     // to be valid for the entire lifetime of
                                     // the borrow.
-                                    break
+                                    break;
                                 }
                                 hir::Mutability::MutMutable => {
                                     // Mutable reference. We *do* need the base
@@ -199,19 +314,19 @@ fn add_reborrow_constraint(
                         }
                         ty::TyRawPtr(..) => {
                             // deref of raw pointer, guaranteed to be valid
-                            break
+                            break;
                         }
                         ty::TyAdt(def, _) if def.is_box() => {
                             // deref of `Box`, need the base to be valid - propagate
                         }
-                        _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place)
+                        _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place),
                     }
                 }
-                ProjectionElem::Field(..) |
-                ProjectionElem::Downcast(..) |
-                ProjectionElem::Index(..) |
-                ProjectionElem::ConstantIndex { .. } |
-                ProjectionElem::Subslice { .. } => {
+                ProjectionElem::Field(..)
+                | ProjectionElem::Downcast(..)
+                | ProjectionElem::Index(..)
+                | ProjectionElem::ConstantIndex { .. }
+                ProjectionElem::Subslice { .. } => {
                     // other field access
                 }
             }
diff --git a/src/librustc_mir/borrow_check/nll/facts.rs b/src/librustc_mir/borrow_check/nll/facts.rs
new file mode 100644 (file)
index 0000000..2802aa0
--- /dev/null
@@ -0,0 +1,194 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use borrow_check::location::{LocationIndex, LocationTable};
+use dataflow::indexes::BorrowIndex;
+use rustc::ty::RegionVid;
+use std::error::Error;
+use std::fmt::Debug;
+use std::fs::{self, File};
+use std::io::Write;
+use std::path::Path;
+
+/// The "facts" which are the basis of the NLL borrow analysis.
+#[derive(Default)]
+crate struct AllFacts {
+    // `borrow_region(R, B, P)` -- the region R may refer to data from borrow B
+    // starting at the point P (this is usually the point *after* a borrow rvalue)
+    crate borrow_region: Vec<(RegionVid, BorrowIndex, LocationIndex)>,
+
+    // universal_region(R) -- this is a "free region" within fn body
+    crate universal_region: Vec<RegionVid>,
+
+    // `cfg_edge(P,Q)` for each edge P -> Q in the control flow
+    crate cfg_edge: Vec<(LocationIndex, LocationIndex)>,
+
+    // `killed(B,P)` when some prefix of the path borrowed at B is assigned at point P
+    crate killed: Vec<(BorrowIndex, LocationIndex)>,
+
+    // `outlives(R1, R2, P)` when we require `R1@P: R2@P`
+    crate outlives: Vec<(RegionVid, RegionVid, LocationIndex)>,
+
+    // `region_live_at(R, P)` when the region R appears in a live variable at P
+    crate region_live_at: Vec<(RegionVid, LocationIndex)>,
+}
+
+impl AllFacts {
+    crate fn write_to_dir(
+        &self,
+        dir: impl AsRef<Path>,
+        location_table: &LocationTable,
+    ) -> Result<(), Box<dyn Error>> {
+        let dir: &Path = dir.as_ref();
+        fs::create_dir_all(dir)?;
+        let wr = FactWriter { location_table, dir };
+        macro_rules! write_facts_to_path {
+            ($wr:ident . write_facts_to_path($this:ident . [
+                $($field:ident,)*
+            ])) => {
+                $(
+                    $wr.write_facts_to_path(
+                        &$this.$field,
+                        &format!("{}.facts", stringify!($field))
+                    )?;
+                )*
+            }
+        }
+        write_facts_to_path! {
+            wr.write_facts_to_path(self.[
+                borrow_region,
+                universal_region,
+                cfg_edge,
+                killed,
+                outlives,
+                region_live_at,
+            ])
+        }
+        Ok(())
+    }
+}
+
+struct FactWriter<'w> {
+    location_table: &'w LocationTable,
+    dir: &'w Path,
+}
+
+impl<'w> FactWriter<'w> {
+    fn write_facts_to_path<T>(
+        &self,
+        rows: &Vec<T>,
+        file_name: &str,
+    ) -> Result<(), Box<dyn Error>>
+    where
+        T: FactRow,
+    {
+        let file = &self.dir.join(file_name);
+        let mut file = File::create(file)?;
+        for row in rows {
+            row.write(&mut file, self.location_table)?;
+        }
+        Ok(())
+    }
+}
+
+trait FactRow {
+    fn write(
+        &self,
+        out: &mut File,
+        location_table: &LocationTable,
+    ) -> Result<(), Box<dyn Error>>;
+}
+
+impl FactRow for RegionVid {
+    fn write(
+        &self,
+        out: &mut File,
+        location_table: &LocationTable,
+    ) -> Result<(), Box<dyn Error>> {
+        write_row(out, location_table, &[self])
+    }
+}
+
+impl<A, B> FactRow for (A, B)
+where
+    A: FactCell,
+    B: FactCell,
+{
+    fn write(
+        &self,
+        out: &mut File,
+        location_table: &LocationTable,
+    ) -> Result<(), Box<dyn Error>> {
+        write_row(out, location_table, &[&self.0, &self.1])
+    }
+}
+
+impl<A, B, C> FactRow for (A, B, C)
+where
+    A: FactCell,
+    B: FactCell,
+    C: FactCell,
+{
+    fn write(
+        &self,
+        out: &mut File,
+        location_table: &LocationTable,
+    ) -> Result<(), Box<dyn Error>> {
+        write_row(out, location_table, &[&self.0, &self.1, &self.2])
+    }
+}
+
+impl<A, B, C, D> FactRow for (A, B, C, D)
+where
+    A: FactCell,
+    B: FactCell,
+    C: FactCell,
+    D: FactCell,
+{
+    fn write(
+        &self,
+        out: &mut File,
+        location_table: &LocationTable,
+    ) -> Result<(), Box<dyn Error>> {
+        write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3])
+    }
+}
+
+fn write_row(
+    out: &mut dyn Write,
+    location_table: &LocationTable,
+    columns: &[&dyn FactCell],
+) -> Result<(), Box<dyn Error>> {
+    for (index, c) in columns.iter().enumerate() {
+        let tail = if index == columns.len() - 1 {
+            "\n"
+        } else {
+            "\t"
+        };
+        write!(out, "{:?}{}", c.to_string(location_table), tail)?;
+    }
+    Ok(())
+}
+
+trait FactCell {
+    fn to_string(&self, location_table: &LocationTable) -> String;
+}
+
+impl<A: Debug> FactCell for A {
+    default fn to_string(&self, _location_table: &LocationTable) -> String {
+        format!("{:?}", self)
+    }
+}
+
+impl FactCell for LocationIndex {
+    fn to_string(&self, location_table: &LocationTable) -> String {
+        format!("{:?}", location_table.to_location(*self))
+    }
+}
index 80ad3ecb9f85ffc1e150e5856fc7dec8d37a94ea..0b1729294d8494b3ba74e8c61b482015f0789977 100644 (file)
 
 use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::LocationTable;
+use dataflow::move_paths::MoveData;
+use dataflow::FlowAtLocation;
+use dataflow::MaybeInitializedPlaces;
 use rustc::hir::def_id::DefId;
-use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir};
 use rustc::infer::InferCtxt;
+use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Mir};
 use rustc::ty::{self, RegionKind, RegionVid};
 use rustc::util::nodemap::FxHashMap;
 use std::collections::BTreeSet;
 use std::fmt::Debug;
 use std::io;
+use std::path::PathBuf;
 use transform::MirSource;
 use util::liveness::{LivenessResults, LocalSet};
-use dataflow::FlowAtLocation;
-use dataflow::MaybeInitializedPlaces;
-use dataflow::move_paths::MoveData;
 
+use self::mir_util::PassWhere;
 use util as mir_util;
 use util::pretty::{self, ALIGN};
-use self::mir_util::PassWhere;
 
 mod constraint_generation;
 pub mod explain_borrow;
+mod facts;
 crate mod region_infer;
 mod renumber;
 mod subtype_constraint_generation;
 crate mod type_check;
 mod universal_regions;
 
+use self::facts::AllFacts;
 use self::region_infer::RegionInferenceContext;
 use self::universal_regions::UniversalRegions;
 
@@ -71,11 +74,11 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     def_id: DefId,
     universal_regions: UniversalRegions<'tcx>,
     mir: &Mir<'tcx>,
-    _location_table: &LocationTable,
+    location_table: &LocationTable,
     param_env: ty::ParamEnv<'gcx>,
     flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
     move_data: &MoveData<'tcx>,
-    _borrow_set: &BorrowSet<'tcx>,
+    borrow_set: &BorrowSet<'tcx>,
 ) -> (
     RegionInferenceContext<'tcx>,
     Option<ClosureRegionRequirements<'gcx>>,
@@ -93,15 +96,47 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
         move_data,
     );
 
+    let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts {
+        Some(AllFacts::default())
+    } else {
+        None
+    };
+
+    if let Some(all_facts) = &mut all_facts {
+        all_facts
+            .universal_region
+            .extend(universal_regions.universal_regions());
+    }
+
     // Create the region inference context, taking ownership of the region inference
     // data that was contained in `infcx`.
     let var_origins = infcx.take_region_var_origins();
-    let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
-    subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
-
+    let mut regioncx =
+        RegionInferenceContext::new(var_origins, universal_regions, mir);
+
+    // Generate various constraints.
+    subtype_constraint_generation::generate(
+        &mut regioncx,
+        &mut all_facts,
+        location_table,
+        mir,
+        constraint_sets,
+    );
+    constraint_generation::generate_constraints(
+        infcx,
+        &mut regioncx,
+        &mut all_facts,
+        location_table,
+        &mir,
+        borrow_set,
+    );
 
-    // Generate non-subtyping constraints.
-    constraint_generation::generate_constraints(infcx, &mut regioncx, &mir);
+    // Dump facts if requested.
+    if let Some(all_facts) = all_facts {
+        let def_path = infcx.tcx.hir.def_path(def_id);
+        let dir_path = PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate());
+        all_facts.write_to_dir(dir_path, location_table).unwrap();
+    }
 
     // Solve the region constraints.
     let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
index 05b61f25a398c1debff978feaaed0be72e943f28..9db19085a39654b630dc4833d9a4b09fb2c45c70 100644 (file)
@@ -8,14 +8,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rustc::mir::{Location, Mir};
+use borrow_check::location::LocationTable;
+use borrow_check::nll::facts::AllFacts;
 use rustc::infer::region_constraints::Constraint;
 use rustc::infer::region_constraints::RegionConstraintData;
 use rustc::infer::region_constraints::{Verify, VerifyBound};
+use rustc::mir::{Location, Mir};
 use rustc::ty;
+use std::iter;
 use syntax::codemap::Span;
 
-use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest};
+use super::region_infer::{RegionInferenceContext, RegionTest, TypeTest};
 use super::type_check::Locations;
 use super::type_check::MirTypeckRegionConstraints;
 use super::type_check::OutlivesSet;
 /// them into the NLL `RegionInferenceContext`.
 pub(super) fn generate<'tcx>(
     regioncx: &mut RegionInferenceContext<'tcx>,
+    all_facts: &mut Option<AllFacts>,
+    location_table: &LocationTable,
     mir: &Mir<'tcx>,
     constraints: &MirTypeckRegionConstraints<'tcx>,
 ) {
-    SubtypeConstraintGenerator { regioncx, mir }.generate(constraints);
+    SubtypeConstraintGenerator {
+        regioncx,
+        location_table,
+        mir,
+    }.generate(constraints, all_facts);
 }
 
 struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
     regioncx: &'cx mut RegionInferenceContext<'tcx>,
+    location_table: &'cx LocationTable,
     mir: &'cx Mir<'tcx>,
 }
 
 impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
-    fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
+    fn generate(
+        &mut self,
+        constraints: &MirTypeckRegionConstraints<'tcx>,
+        all_facts: &mut Option<AllFacts>,
+    ) {
         let MirTypeckRegionConstraints {
             liveness_set,
             outlives_sets,
@@ -57,6 +71,17 @@ fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
             self.regioncx.add_live_point(region_vid, *location, &cause);
         }
 
+        if let Some(all_facts) = all_facts {
+            all_facts
+                .region_live_at
+                .extend(liveness_set.into_iter().flat_map(|(region, location, _)| {
+                    let r = self.to_region_vid(region);
+                    let p1 = self.location_table.start_index(*location);
+                    let p2 = self.location_table.mid_index(*location);
+                    iter::once((r, p1)).chain(iter::once((r, p2)))
+                }));
+        }
+
         for OutlivesSet { locations, data } in outlives_sets {
             debug!("generate: constraints at: {:#?}", locations);
             let RegionConstraintData {
@@ -88,6 +113,23 @@ fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
                 // "outlives" (`>=`) whereas the region constraints
                 // talk about `<=`.
                 self.regioncx.add_outlives(span, b_vid, a_vid, at_location);
+
+                // In the new analysis, all outlives relations etc
+                // "take effect" at the mid point of the statement
+                // that requires them, so ignore the `at_location`.
+                if let Some(all_facts) = all_facts {
+                    if let Some(from_location) = locations.from_location() {
+                        all_facts.outlives.push((
+                            b_vid,
+                            a_vid,
+                            self.location_table.mid_index(from_location),
+                        ));
+                    } else {
+                        for location in self.location_table.all_points() {
+                            all_facts.outlives.push((b_vid, a_vid, location));
+                        }
+                    }
+                }
             }
 
             for verify in verifys {
index 95cf3b8ddc6ceace3095a749d965e07a3b36344e..2545ba3a94af12215c6a4a15437304944a2ee358 100644 (file)
@@ -34,6 +34,7 @@
 #![feature(inclusive_range_methods)]
 #![feature(crate_visibility_modifier)]
 #![feature(never_type)]
+#![feature(specialization)]
 #![cfg_attr(stage0, feature(try_trait))]
 
 extern crate arena;