]> git.lizzy.rs Git - rust.git/commitdiff
integrate NLL with MIR type-checker
authorNiko Matsakis <niko@alum.mit.edu>
Mon, 6 Nov 2017 12:26:34 +0000 (07:26 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 16 Nov 2017 10:57:47 +0000 (05:57 -0500)
src/librustc/middle/free_region.rs
src/librustc_mir/borrow_check.rs
src/librustc_mir/transform/nll/constraint_generation.rs
src/librustc_mir/transform/nll/free_regions.rs
src/librustc_mir/transform/nll/mod.rs
src/librustc_mir/transform/nll/region_infer.rs
src/librustc_mir/transform/nll/subtype.rs [deleted file]
src/librustc_mir/transform/nll/subtype_constraint_generation.rs [new file with mode: 0644]
src/test/ui/nll/get_default.rs [new file with mode: 0644]
src/test/ui/nll/get_default.stderr [new file with mode: 0644]

index 3bcdc4f7e2c630a860a483c733a9adbe69f95b2d..da505f167241b812e6acdad32e1fa528a1f123be 100644 (file)
@@ -192,7 +192,7 @@ pub fn lub_free_regions<'a, 'gcx>(&self,
     ///
     /// if `r_a` represents `'a`, this function would return `{'b, 'c}`.
     pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> {
-        assert!(is_free(r_a));
+        assert!(is_free(r_a) || *r_a == ty::ReStatic);
         self.relation.greater_than(&r_a)
     }
 }
index 70f4e41a9cbfe9a12cb07daf9cd6aefe889c10c7..a72320cdbdd6f69e3e13a1f53c57161e54fcc72d 100644 (file)
@@ -112,7 +112,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
         None
     } else {
-        Some(nll::compute_regions(infcx, def_id, mir))
+        Some(nll::compute_regions(infcx, def_id, param_env, mir))
     };
 
     let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
index 627bc7a8324529fa4438f5053fdb8de2ef537b49..1f905d32f84e84b751ba6f773e7950f0fef2c116 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use rustc::hir;
-use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind};
+use rustc::mir::{Location, Lvalue, Mir, Rvalue};
 use rustc::mir::visit::Visitor;
 use rustc::mir::Lvalue::Projection;
 use rustc::mir::{LvalueProjection, ProjectionElem};
@@ -21,7 +21,6 @@
 use rustc_data_structures::fx::FxHashSet;
 use syntax::codemap::DUMMY_SP;
 
-use super::subtype;
 use super::LivenessResults;
 use super::ToRegionVid;
 use super::region_infer::RegionInferenceContext;
@@ -179,29 +178,6 @@ fn add_borrow_constraints(&mut self) {
         self.visit_mir(self.mir);
     }
 
-    fn add_borrow_constraint(
-        &mut self,
-        location: Location,
-        destination_lv: &Lvalue<'tcx>,
-        borrow_region: ty::Region<'tcx>,
-        _borrow_kind: BorrowKind,
-        _borrowed_lv: &Lvalue<'tcx>,
-    ) {
-        let tcx = self.infcx.tcx;
-        let span = self.mir.source_info(location).span;
-        let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
-
-        let destination_region = match destination_ty.sty {
-            ty::TyRef(r, _) => r,
-            _ => bug!()
-        };
-
-        self.regioncx.add_outlives(span,
-                                   borrow_region.to_region_vid(),
-                                   destination_region.to_region_vid(),
-                                   location.successor_within_block());
-    }
-
     fn add_reborrow_constraint(
         &mut self,
         location: Location,
@@ -237,35 +213,22 @@ fn add_reborrow_constraint(
 }
 
 impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
-    fn visit_statement(&mut self,
-                       block: BasicBlock,
-                       statement: &Statement<'tcx>,
-                       location: Location) {
+    fn visit_rvalue(&mut self,
+                    rvalue: &Rvalue<'tcx>,
+                    location: Location) {
+        debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
 
-        debug!("visit_statement(statement={:?}, location={:?})", statement, location);
-
-        // Look for a statement like:
+        // Look for an rvalue like:
         //
-        //     D = & L
+        //     & L
         //
-        // where D is the path to which we are assigning, and
-        // L is the path that is borrowed.
-        if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind {
-            if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv {
-                self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv);
-                self.add_reborrow_constraint(location, region, borrowed_lv);
-            }
-
-            let tcx = self.infcx.tcx;
-            let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
-            let rv_ty = rv.ty(self.mir, tcx);
-
-            let span = self.mir.source_info(location).span;
-            for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
-                self.regioncx.add_outlives(span, a, b, location.successor_within_block());
-            }
+        // 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);
         }
 
-        self.super_statement(block, statement, location);
+        self.super_rvalue(rvalue, location);
     }
 }
index 101fed3cfa631463d478e0bb4b122f0f9dc36086..92a8a714d525a09a369a08f55167c77879613f00 100644 (file)
@@ -50,6 +50,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>(
 
     let mut indices = FxHashMap();
 
+    // `'static` is always free.
+    insert_free_region(&mut indices, infcx.tcx.types.re_static);
+
     // Extract the early regions.
     let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
     for item_subst in item_substs {
index 15c74f49c11610cc5597c98ac5fcb29b82761bd5..147f061ad113f52509fb0368bf5cda605473a545 100644 (file)
 use rustc::util::nodemap::FxHashMap;
 use std::collections::BTreeSet;
 use transform::MirSource;
+use transform::type_check;
 use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
 
 use util as mir_util;
 use self::mir_util::PassWhere;
 
 mod constraint_generation;
+mod subtype_constraint_generation;
 mod free_regions;
-mod subtype;
 
 pub(crate) mod region_infer;
 use self::region_infer::RegionInferenceContext;
@@ -35,6 +36,7 @@
 pub fn compute_regions<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     def_id: DefId,
+    param_env: ty::ParamEnv<'gcx>,
     mir: &mut Mir<'tcx>,
 ) -> RegionInferenceContext<'tcx> {
     // Compute named region information.
@@ -43,6 +45,16 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
     // Replace all regions with fresh inference variables.
     renumber::renumber_mir(infcx, free_regions, mir);
 
+    // Run the MIR type-checker.
+    let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
+    let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
+
+    // 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, free_regions, mir);
+    subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
+
     // Compute what is live where.
     let liveness = &LivenessResults {
         regular: liveness::liveness_of_locals(
@@ -62,12 +74,10 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
         ),
     };
 
-    // Create the region inference context, generate the constraints,
-    // and then solve them.
-    let var_origins = infcx.take_region_var_origins();
-    let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
-    let param_env = infcx.tcx.param_env(def_id);
+    // Generate non-subtyping constraints.
     constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
+
+    // Solve the region constraints.
     regioncx.solve(infcx, &mir);
 
     // Dump MIR results into a file, if that is enabled. This let us
@@ -123,12 +133,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
         match pass_where {
             // Before the CFG, dump out the values for each region variable.
             PassWhere::BeforeCFG => for region in regioncx.regions() {
-                writeln!(
-                    out,
-                    "| {:?}: {:?}",
-                    region,
-                    regioncx.region_value(region)
-                )?;
+                writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?;
             },
 
             // Before each basic block, dump out the values
index 566a3b23ff13869e3dca5cd3488c1f1d965016b7..f218cc51d344b83f01760c7fb5e68cc5826a0a48 100644 (file)
@@ -183,6 +183,15 @@ fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx
             // Add `end(X)` into the set for X.
             self.definitions[variable].value.add_free_region(variable);
 
+            // `'static` outlives all other free regions as well.
+            if let ty::ReStatic = free_region {
+                for &other_variable in indices.values() {
+                    self.definitions[variable]
+                        .value
+                        .add_free_region(other_variable);
+                }
+            }
+
             // Go through each region Y that outlives X (i.e., where
             // Y: X is true). Add `end(X)` into the set for `Y`.
             for superregion in free_region_map.regions_that_outlive(&free_region) {
diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs
deleted file mode 100644 (file)
index bb41477..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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 transform::nll::ToRegionVid;
-use rustc::ty::{self, Ty, TyCtxt, RegionVid};
-use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
-
-pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                      a: Ty<'tcx>,
-                      b: Ty<'tcx>)
-                      -> Vec<(RegionVid, RegionVid)>
-{
-    let mut subtype = Subtype::new(tcx);
-    match subtype.relate(&a, &b) {
-        Ok(_) => subtype.outlives_pairs,
-
-        Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b)
-    }
-}
-
-struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'gcx, 'tcx>,
-    outlives_pairs: Vec<(RegionVid, RegionVid)>,
-    ambient_variance: ty::Variance,
-}
-
-impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> {
-    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> {
-        Subtype {
-            tcx,
-            outlives_pairs: vec![],
-            ambient_variance: ty::Covariant,
-        }
-    }
-}
-
-impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> {
-    fn tag(&self) -> &'static str { "Subtype" }
-    fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx }
-    fn a_is_expected(&self) -> bool { true } // irrelevant
-
-    fn relate_with_variance<T: Relate<'tcx>>(&mut self,
-                                             variance: ty::Variance,
-                                             a: &T,
-                                             b: &T)
-                                             -> RelateResult<'tcx, T>
-    {
-        let old_ambient_variance = self.ambient_variance;
-        self.ambient_variance = self.ambient_variance.xform(variance);
-
-        let result = self.relate(a, b);
-        self.ambient_variance = old_ambient_variance;
-        result
-    }
-
-    fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-        relate::super_relate_tys(self, t, t2)
-    }
-
-    fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>)
-               -> RelateResult<'tcx, ty::Region<'tcx>> {
-        let a = r_a.to_region_vid();
-        let b = r_b.to_region_vid();
-
-        match self.ambient_variance {
-            ty::Covariant => {
-                self.outlives_pairs.push((b, a));
-            },
-
-            ty::Invariant => {
-                self.outlives_pairs.push((a, b));
-                self.outlives_pairs.push((b, a));
-            },
-
-            ty::Contravariant => {
-                self.outlives_pairs.push((a, b));
-            },
-
-            ty::Bivariant => {},
-        }
-
-        Ok(r_a)
-    }
-
-    fn binders<T>(&mut self, _a: &ty::Binder<T>, _b: &ty::Binder<T>)
-                  -> RelateResult<'tcx, ty::Binder<T>>
-        where T: Relate<'tcx>
-    {
-        unimplemented!();
-    }
-}
diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/transform/nll/subtype_constraint_generation.rs
new file mode 100644 (file)
index 0000000..c1850c7
--- /dev/null
@@ -0,0 +1,112 @@
+// 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 rustc::mir::Mir;
+use rustc::infer::region_constraints::Constraint;
+use rustc::infer::region_constraints::RegionConstraintData;
+use rustc::ty;
+use transform::type_check::MirTypeckRegionConstraints;
+use transform::type_check::OutlivesSet;
+
+use super::free_regions::FreeRegions;
+use super::region_infer::RegionInferenceContext;
+
+/// When the MIR type-checker executes, it validates all the types in
+/// the MIR, and in the process generates a set of constraints that
+/// must hold regarding the regions in the MIR, along with locations
+/// *where* they must hold. This code takes those constriants and adds
+/// them into the NLL `RegionInferenceContext`.
+pub(super) fn generate<'tcx>(
+    regioncx: &mut RegionInferenceContext<'tcx>,
+    free_regions: &FreeRegions<'tcx>,
+    mir: &Mir<'tcx>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+) {
+    SubtypeConstraintGenerator {
+        regioncx,
+        free_regions,
+        mir,
+    }.generate(constraints);
+}
+
+struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
+    regioncx: &'cx mut RegionInferenceContext<'tcx>,
+    free_regions: &'cx FreeRegions<'tcx>,
+    mir: &'cx Mir<'tcx>,
+}
+
+impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
+    fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
+        let MirTypeckRegionConstraints {
+            liveness_set,
+            outlives_sets,
+        } = constraints;
+
+        debug!(
+            "generate(liveness_set={} items, outlives_sets={} items)",
+            liveness_set.len(),
+            outlives_sets.len()
+        );
+
+        for (region, location) in liveness_set {
+            debug!("generate: {:#?} is live at {:#?}", region, location);
+            let region_vid = self.to_region_vid(region);
+            self.regioncx.add_live_point(region_vid, *location);
+        }
+
+        for OutlivesSet { locations, data } in outlives_sets {
+            debug!("generate: constraints at: {:#?}", locations);
+            let RegionConstraintData {
+                constraints,
+                verifys,
+                givens,
+            } = data;
+
+            for constraint in constraints.keys() {
+                debug!("generate: constraint: {:?}", constraint);
+                let (a_vid, b_vid) = match constraint {
+                    Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid),
+                    Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid),
+                    Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)),
+                    Constraint::RegSubReg(a_r, b_r) => {
+                        (self.to_region_vid(a_r), self.to_region_vid(b_r))
+                    }
+                };
+
+                // We have the constraint that `a_vid <= b_vid`. Add
+                // `b_vid: a_vid` to our region checker. Note that we
+                // reverse direction, because `regioncx` talks about
+                // "outlives" (`>=`) whereas the region constraints
+                // talk about `<=`.
+                let span = self.mir.source_info(locations.from_location).span;
+                self.regioncx
+                    .add_outlives(span, b_vid, a_vid, locations.at_location);
+            }
+
+            assert!(verifys.is_empty(), "verifys not yet implemented");
+            assert!(
+                givens.is_empty(),
+                "MIR type-checker does not use givens (thank goodness)"
+            );
+        }
+    }
+
+    fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
+        // Every region that we see in the constraints came from the
+        // MIR or from the parameter environment. If the former, it
+        // will be a region variable.  If the latter, it will be in
+        // the set of free regions *somewhere*.
+        if let ty::ReVar(vid) = r {
+            *vid
+        } else {
+            self.free_regions.indices[&r]
+        }
+    }
+}
diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs
new file mode 100644 (file)
index 0000000..5605206
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2016 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.
+
+// Basic test for free regions in the NLL code. This test ought to
+// report an error due to a reborrowing constraint. Right now, we get
+// a variety of errors from the older, AST-based machinery (notably
+// borrowck), and then we get the NLL error at the end.
+
+// compile-flags:-Znll -Zborrowck-mir
+
+struct Map {
+}
+
+impl Map {
+    fn get(&self) -> Option<&String> { None }
+    fn set(&mut self, v: String) { }
+}
+
+fn ok(map: &mut Map) -> &String {
+    loop {
+        match map.get() {
+            Some(v) => {
+                return v;
+            }
+            None => {
+                map.set(String::new()); // Just AST errors here
+            }
+        }
+    }
+}
+
+fn err(map: &mut Map) -> &String {
+    loop {
+        match map.get() {
+            Some(v) => {
+                map.set(String::new()); // Both AST and MIR error here
+                return v;
+            }
+            None => {
+                map.set(String::new()); // Just AST errors here
+            }
+        }
+    }
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr
new file mode 100644 (file)
index 0000000..9586f42
--- /dev/null
@@ -0,0 +1,47 @@
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
+  --> $DIR/get_default.rs:33:17
+   |
+28 |         match map.get() {
+   |               --- immutable borrow occurs here
+...
+33 |                 map.set(String::new()); // Just AST errors here
+   |                 ^^^ mutable borrow occurs here
+...
+37 | }
+   | - immutable borrow ends here
+
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
+  --> $DIR/get_default.rs:43:17
+   |
+41 |         match map.get() {
+   |               --- immutable borrow occurs here
+42 |             Some(v) => {
+43 |                 map.set(String::new()); // Both AST and MIR error here
+   |                 ^^^ mutable borrow occurs here
+...
+51 | }
+   | - immutable borrow ends here
+
+error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
+  --> $DIR/get_default.rs:47:17
+   |
+41 |         match map.get() {
+   |               --- immutable borrow occurs here
+...
+47 |                 map.set(String::new()); // Just AST errors here
+   |                 ^^^ mutable borrow occurs here
+...
+51 | }
+   | - immutable borrow ends here
+
+error[E0502]: cannot borrow `(*map)` as mutable because it is also borrowed as immutable (Mir)
+  --> $DIR/get_default.rs:43:17
+   |
+41 |         match map.get() {
+   |               --- immutable borrow occurs here
+42 |             Some(v) => {
+43 |                 map.set(String::new()); // Both AST and MIR error here
+   |                 ^^^ mutable borrow occurs here
+
+error: aborting due to 4 previous errors
+