///
/// 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)
}
}
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 };
// 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};
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;
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,
}
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);
}
}
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 {
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;
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.
// 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(
),
};
- // 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
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
// 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) {
+++ /dev/null
-// 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!();
- }
-}
--- /dev/null
+// 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]
+ }
+ }
+}
--- /dev/null
+// 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() { }
--- /dev/null
+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
+