From 932452ecc7f0a8ab8ad678a343d828385de80e1a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Nov 2017 05:25:03 -0500 Subject: [PATCH 01/30] separate out the liveness constraints from the final value It will be useful later for diagnostics to be able to remember where things were live. --- .../transform/nll/region_infer.rs | 81 +++++++++++-------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 1609c1236b0ca..78b6a9eb6bc7b 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -28,6 +28,16 @@ pub struct RegionInferenceContext<'tcx> { /// from as well as its final inferred value. definitions: IndexVec>, + /// The liveness constraints added to each region. For most + /// regions, these start out empty and steadily grow, though for + /// each free region R they start out containing the entire CFG + /// and `end(R)`. + liveness_constraints: IndexVec, + + /// The final inferred values of the inference variables; `None` + /// until `solve` is invoked. + inferred_values: Option>, + /// The constraints we have accumulated and used during solving. constraints: Vec, } @@ -46,11 +56,6 @@ struct RegionDefinition<'tcx> { /// If true, this is a constant region which cannot grow larger. /// This is used for named regions as well as `'static`. constant: bool, - - /// The current value of this inference variable. This starts out - /// empty, but grows as we add constraints. The final value is - /// determined when `solve()` is executed. - value: Region, } /// The value of an individual region variable. Region variables @@ -115,6 +120,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// of those will be constant regions representing the free /// regions defined in `free_regions`. pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self { + let num_region_variables = var_origins.len(); + // Create a RegionDefinition for each inference variable. let definitions = var_origins .into_iter() @@ -123,6 +130,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { let mut result = Self { definitions: definitions, + liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables), + inferred_values: None, constraints: Vec::new(), }; @@ -170,24 +179,23 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // Add all nodes in the CFG to `definition.value`. for (block, block_data) in mir.basic_blocks().iter_enumerated() { - let definition = &mut self.definitions[variable]; + let liveness_constraint = &mut self.liveness_constraints[variable]; for statement_index in 0..block_data.statements.len() + 1 { let location = Location { block, statement_index, }; - definition.value.add_point(location); + liveness_constraint.add_point(location); } } // Add `end(X)` into the set for X. - self.definitions[variable].value.add_free_region(variable); + self.liveness_constraints[variable].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 + self.liveness_constraints[variable] .add_free_region(other_variable); } } @@ -196,16 +204,14 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // Y: X is true). Add `end(X)` into the set for `Y`. for superregion in free_region_map.regions_that_outlive(&free_region) { let superregion_index = indices[superregion]; - self.definitions[superregion_index] - .value - .add_free_region(variable); + self.liveness_constraints[superregion_index].add_free_region(variable); } debug!( "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`", free_region, variable, - self.definitions[variable].value + self.liveness_constraints[variable], ); } } @@ -219,25 +225,25 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// /// Until `solve()` executes, this value is not particularly meaningful. pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { - self.definitions[r].value.contains_point(p) + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + inferred_values[r].contains_point(p) } /// Returns access to the value of `r` for debugging purposes. pub(super) fn region_value(&self, r: RegionVid) -> &fmt::Debug { - &self.definitions[r].value + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + &inferred_values[r] } /// Indicates that the region variable `v` is live at the point `point`. pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) { debug!("add_live_point({:?}, {:?})", v, point); - let definition = &mut self.definitions[v]; - if !definition.constant { - definition.value.add_point(point); - } else { - // Constants are used for free regions, which already - // contain all the points in the control-flow graph. - assert!(definition.value.contains_point(point)); - } + assert!(self.inferred_values.is_none(), "values already inferred"); + self.liveness_constraints[v].add_point(point); } /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`. @@ -249,6 +255,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { point: Location, ) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); + assert!(self.inferred_values.is_none(), "values already inferred"); self.constraints.push(Constraint { span, sup, @@ -259,6 +266,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// Perform region inference. pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>) { + assert!(self.inferred_values.is_none(), "values already inferred"); let errors = self.propagate_constraints(mir); // worst error msg ever @@ -291,39 +299,43 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { constraints }); + // The initial values for each region are derived from the liveness + // constraints we have accumulated. + let mut inferred_values = self.liveness_constraints.clone(); + while changed { changed = false; for constraint in &self.constraints { debug!("propagate_constraints: constraint={:?}", constraint); - let sub = &self.definitions[constraint.sub].value.clone(); - let sup_def = &mut self.definitions[constraint.sup]; + let sub = &inferred_values[constraint.sub].clone(); + let sup_value = &mut inferred_values[constraint.sup]; debug!("propagate_constraints: sub (before): {:?}", sub); - debug!("propagate_constraints: sup (before): {:?}", sup_def.value); + debug!("propagate_constraints: sup (before): {:?}", sup_value); - if !sup_def.constant { + if !self.definitions[constraint.sup].constant { // If this is not a constant, then grow the value as needed to // accommodate the outlives constraint. - if dfs.copy(sub, &mut sup_def.value, constraint.point) { + if dfs.copy(sub, sup_value, constraint.point) { changed = true; } - debug!("propagate_constraints: sup (after) : {:?}", sup_def.value); + debug!("propagate_constraints: sup (after) : {:?}", sup_value); debug!("propagate_constraints: changed : {:?}", changed); } else { // If this is a constant, check whether it *would // have* to grow in order for the constraint to be // satisfied. If so, create an error. - let mut sup_value = sup_def.value.clone(); - if dfs.copy(sub, &mut sup_value, constraint.point) { + let sup_value = &mut sup_value.clone(); + if dfs.copy(sub, sup_value, constraint.point) { // Constant values start out with the entire // CFG, so it must be some new free region // that was added. Find one. let &new_region = sup_value .free_regions - .difference(&sup_def.value.free_regions) + .difference(&sup_value.free_regions) .next() .unwrap(); debug!("propagate_constraints: new_region : {:?}", new_region); @@ -335,6 +347,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { } debug!("\n"); } + + self.inferred_values = Some(inferred_values); errors } } @@ -424,7 +438,6 @@ impl<'tcx> RegionDefinition<'tcx> { origin, name: None, constant: false, - value: Region::default(), } } } From a96b0cf86df86c9b9f9bbc5a24d06e4304bc871d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Nov 2017 06:04:49 -0500 Subject: [PATCH 02/30] replace constant regions with a post-inference check Rather than declaring some region variables to be constant, and reporting errors when they would have to change, we instead populate each free region X with a minimal set of points (the CFG plus end(X)), and then we let inference do its thing. This may add other `end(Y)` points into X; we can then check after the fact that indeed `X: Y` holds. This requires a bit of "blame" detection to find where the bad constraint came from: we are currently using a pretty dumb algorithm. Good place for later expansion. --- src/librustc/middle/free_region.rs | 51 +++-- .../transform/nll/region_infer.rs | 208 +++++++++++------- src/test/mir-opt/nll/named-lifetimes-basic.rs | 4 +- 3 files changed, 163 insertions(+), 100 deletions(-) diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 89c3f1668474a..3f0e6e2c28dd0 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -63,28 +63,28 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> { -> bool { let result = sub_region == super_region || { match (sub_region, super_region) { - (&ty::ReEmpty, _) | - (_, &ty::ReStatic) => + (ty::ReEmpty, _) | + (_, ty::ReStatic) => true, - (&ty::ReScope(sub_scope), &ty::ReScope(super_scope)) => - self.region_scope_tree.is_subscope_of(sub_scope, super_scope), + (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => + self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope), - (&ty::ReScope(sub_scope), &ty::ReEarlyBound(ref br)) => { + (ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => { let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br); - self.region_scope_tree.is_subscope_of(sub_scope, fr_scope) + self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope) } - (&ty::ReScope(sub_scope), &ty::ReFree(ref fr)) => { + (ty::ReScope(sub_scope), ty::ReFree(fr)) => { let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr); - self.region_scope_tree.is_subscope_of(sub_scope, fr_scope) + self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope) } - (&ty::ReEarlyBound(_), &ty::ReEarlyBound(_)) | - (&ty::ReFree(_), &ty::ReEarlyBound(_)) | - (&ty::ReEarlyBound(_), &ty::ReFree(_)) | - (&ty::ReFree(_), &ty::ReFree(_)) => - self.free_regions.sub_free_regions(&sub_region, &super_region), + (ty::ReEarlyBound(_), ty::ReEarlyBound(_)) | + (ty::ReFree(_), ty::ReEarlyBound(_)) | + (ty::ReEarlyBound(_), ty::ReFree(_)) | + (ty::ReFree(_), ty::ReFree(_)) => + self.free_regions.sub_free_regions(sub_region, super_region), _ => false, @@ -162,23 +162,23 @@ impl<'tcx> FreeRegionMap<'tcx> { /// (with the exception that `'static: 'x` is not notable) pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) { + if is_free_or_static(sub) && is_free(sup) { self.relation.add(sub, sup) } } - /// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b` - /// must be free regions from the function header. + /// Tests whether `r_a <= sup`. Both must be free regions or + /// `'static`. pub fn sub_free_regions<'a, 'gcx>(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { - debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); - assert!(is_free(r_a)); - assert!(is_free(r_b)); - let result = r_a == r_b || self.relation.contains(&r_a, &r_b); - debug!("sub_free_regions: result={}", result); - result + assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); + if let ty::ReStatic = r_b { + true // `'a <= 'static` is just always true, and not stored in the relation explicitly + } else { + r_a == r_b || self.relation.contains(&r_a, &r_b) + } } /// Compute the least-upper-bound of two free regions. In some @@ -224,6 +224,13 @@ fn is_free(r: Region) -> bool { } } +fn is_free_or_static(r: Region) -> bool { + match *r { + ty::ReStatic => true, + _ => is_free(r), + } +} + impl_stable_hash_for!(struct FreeRegionMap<'tcx> { relation }); diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 78b6a9eb6bc7b..f60bd3c6ecec5 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -13,6 +13,7 @@ use rustc::infer::InferCtxt; use rustc::infer::RegionVariableOrigin; use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::region_constraints::VarOrigins; +use rustc::middle::free_region::FreeRegionMap; use rustc::mir::{Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; @@ -40,6 +41,8 @@ pub struct RegionInferenceContext<'tcx> { /// The constraints we have accumulated and used during solving. constraints: Vec, + + free_region_map: &'tcx FreeRegionMap<'tcx>, } struct RegionDefinition<'tcx> { @@ -52,10 +55,6 @@ struct RegionDefinition<'tcx> { /// If this is a free-region, then this is `Some(X)` where `X` is /// the name of the region. name: Option>, - - /// If true, this is a constant region which cannot grow larger. - /// This is used for named regions as well as `'static`. - constant: bool, } /// The value of an individual region variable. Region variables @@ -100,7 +99,6 @@ pub struct Constraint { // it is for convenience. Before we dump the constraints in the // debugging logs, we sort them, and we'd like the "super region" // to be first, etc. (In particular, span should remain last.) - /// The region SUP must outlive SUB... sup: RegionVid, @@ -114,7 +112,7 @@ pub struct Constraint { span: Span, } -impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { +impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free @@ -133,6 +131,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables), inferred_values: None, constraints: Vec::new(), + free_region_map: free_regions.free_region_map, }; result.init_free_regions(free_regions, mir); @@ -162,7 +161,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) { let FreeRegions { indices, - free_region_map, + free_region_map: _, } = free_regions; // For each free region X: @@ -175,7 +174,6 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // Initialize the name and a few other details. self.definitions[variable].name = Some(free_region); - self.definitions[variable].constant = true; // Add all nodes in the CFG to `definition.value`. for (block, block_data) in mir.basic_blocks().iter_enumerated() { @@ -192,21 +190,6 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // Add `end(X)` into the set for X. self.liveness_constraints[variable].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.liveness_constraints[variable] - .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) { - let superregion_index = indices[superregion]; - self.liveness_constraints[superregion_index].add_free_region(variable); - } - debug!( "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`", free_region, @@ -265,20 +248,72 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { } /// Perform region inference. - pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>) { + pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { assert!(self.inferred_values.is_none(), "values already inferred"); - let errors = self.propagate_constraints(mir); - - // worst error msg ever - for (fr1, span, fr2) in errors { - infcx.tcx.sess.span_err( - span, - &format!( - "free region `{}` does not outlive `{}`", - self.definitions[fr1].name.unwrap(), - self.definitions[fr2].name.unwrap() - ), - ); + + // Find the minimal regions that can solve the constraints. This is infallible. + self.propagate_constraints(mir); + + // Now, see whether any of the constraints were too strong. In particular, + // we want to check for a case where a free region exceeded its bounds. + // Consider: + // + // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + // + // In this case, returning `x` requires `&'a u32 <: &'b u32` + // and hence we establish (transitively) a constraint that + // `'a: 'b`. The `propagate_constraints` code above will + // therefore add `end('a)` into the region for `'b` -- but we + // have no evidence that `'b` outlives `'a`, so we want to report + // an error. + + // The free regions are always found in a prefix of the full list. + let free_region_definitions = self.definitions + .iter_enumerated() + .take_while(|(_, fr_definition)| fr_definition.name.is_some()); + + for (fr, fr_definition) in free_region_definitions { + self.check_free_region(infcx, fr, fr_definition); + } + } + + fn check_free_region( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + fr: RegionVid, + fr_definition: &RegionDefinition<'tcx>, + ) { + let inferred_values = self.inferred_values.as_ref().unwrap(); + let fr_name = fr_definition.name.unwrap(); + let fr_value = &inferred_values[fr]; + + // Find every region `o` such that `fr: o` + // (because `fr` includes `end(o)`). + for &outlived_fr in &fr_value.free_regions { + // `fr` includes `end(fr)`, that's not especially + // interesting. + if fr == outlived_fr { + continue; + } + + let outlived_fr_definition = &self.definitions[outlived_fr]; + let outlived_fr_name = outlived_fr_definition.name.unwrap(); + + // Check that `o <= fr`. If not, report an error. + if !self.free_region_map + .sub_free_regions(outlived_fr_name, fr_name) + { + // worst error msg ever + let blame_span = self.blame_span(fr, outlived_fr); + infcx.tcx.sess.span_err( + blame_span, + &format!( + "free region `{}` does not outlive `{}`", + fr_name, + outlived_fr_name + ), + ); + } } } @@ -286,11 +321,9 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. - fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, RegionVid)> { + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { let mut changed = true; let mut dfs = Dfs::new(mir); - let mut error_regions = FxHashSet(); - let mut errors = vec![]; debug!("propagate_constraints()"); debug!("propagate_constraints: constraints={:#?}", { @@ -305,51 +338,78 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { while changed { changed = false; + debug!("propagate_constraints: --------------------"); for constraint in &self.constraints { debug!("propagate_constraints: constraint={:?}", constraint); + let sub = &inferred_values[constraint.sub].clone(); let sup_value = &mut inferred_values[constraint.sup]; - debug!("propagate_constraints: sub (before): {:?}", sub); - debug!("propagate_constraints: sup (before): {:?}", sup_value); + // Grow the value as needed to accommodate the + // outlives constraint. - if !self.definitions[constraint.sup].constant { - // If this is not a constant, then grow the value as needed to - // accommodate the outlives constraint. + if dfs.copy(sub, sup_value, constraint.point) { + debug!("propagate_constraints: sub={:?}", sub); + debug!("propagate_constraints: sup={:?}", sup_value); + changed = true; + } + } + debug!("\n"); + } - if dfs.copy(sub, sup_value, constraint.point) { - changed = true; - } + self.inferred_values = Some(inferred_values); + } - debug!("propagate_constraints: sup (after) : {:?}", sup_value); - debug!("propagate_constraints: changed : {:?}", changed); - } else { - // If this is a constant, check whether it *would - // have* to grow in order for the constraint to be - // satisfied. If so, create an error. - - let sup_value = &mut sup_value.clone(); - if dfs.copy(sub, sup_value, constraint.point) { - // Constant values start out with the entire - // CFG, so it must be some new free region - // that was added. Find one. - let &new_region = sup_value - .free_regions - .difference(&sup_value.free_regions) - .next() - .unwrap(); - debug!("propagate_constraints: new_region : {:?}", new_region); - if error_regions.insert(constraint.sup) { - errors.push((constraint.sup, constraint.span, new_region)); - } + /// Tries to finds a good span to blame for the fact that `fr1` + /// contains `fr2`. + fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { + // Find everything that influenced final value of `fr`. + let influenced_fr1 = self.dependencies(fr1); + + // Try to find some outlives constraint `'X: fr2` where `'X` + // influenced `fr1`. Blame that. + // + // NB, this is a pretty bad choice most of the time. In + // particular, the connection between `'X` and `fr1` may not + // be obvious to the user -- not to mention the naive notion + // of dependencies, which doesn't account for the locations of + // contraints at all. But it will do for now. + for constraint in &self.constraints { + if constraint.sub == fr2 && influenced_fr1[constraint.sup] { + return constraint.span; + } + } + + bug!( + "could not find any constraint to blame for {:?}: {:?}", + fr1, + fr2 + ); + } + + /// Finds all regions whose values `'a` may depend on in some way. + /// Basically if there exists a constraint `'a: 'b @ P`, then `'b` + /// and `dependencies('b)` will be in the final set. + /// + /// Used during error reporting, extremely naive and inefficient. + fn dependencies(&self, r0: RegionVid) -> IndexVec { + let mut result_set = IndexVec::from_elem(false, &self.definitions); + let mut changed = true; + result_set[r0] = true; + + while changed { + changed = false; + for constraint in &self.constraints { + if result_set[constraint.sup] { + if !result_set[constraint.sub] { + result_set[constraint.sub] = true; + changed = true; } } } - debug!("\n"); } - self.inferred_values = Some(inferred_values); - errors + result_set } } @@ -434,11 +494,7 @@ impl<'tcx> RegionDefinition<'tcx> { // Create a new region definition. Note that, for free // regions, these fields get updated later in // `init_free_regions`. - Self { - origin, - name: None, - constant: false, - } + Self { origin, name: None } } } diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 34d0482a623a9..7039de727faa9 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -26,9 +26,9 @@ fn main() { // END RUST SOURCE // START rustc.use_x.nll.0.mir -// | '_#0r: {bb0[0], bb0[1], '_#0r, '_#1r, '_#2r, '_#3r} +// | '_#0r: {bb0[0], bb0[1], '_#0r} // | '_#1r: {bb0[0], bb0[1], '_#1r} -// | '_#2r: {bb0[0], bb0[1], '_#1r, '_#2r} +// | '_#2r: {bb0[0], bb0[1], '_#2r} // | '_#3r: {bb0[0], bb0[1], '_#3r} // fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { // END rustc.use_x.nll.0.mir From 08c8d7e91917dd7265042d6ccf204e241f17db32 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Nov 2017 04:47:02 -0500 Subject: [PATCH 03/30] borrow_check.rs: rustfmt --- src/librustc_mir/borrow_check.rs | 1708 ++++++++++++++++++------------ 1 file changed, 1027 insertions(+), 681 deletions(-) diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index ee48d1d369d53..ff38760cce699 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -11,30 +11,30 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. use rustc::hir; -use rustc::hir::def_id::{DefId}; -use rustc::infer::{InferCtxt}; -use rustc::ty::{self, TyCtxt, ParamEnv}; +use rustc::hir::def_id::DefId; +use rustc::infer::InferCtxt; +use rustc::ty::{self, ParamEnv, TyCtxt}; use rustc::ty::maps::Providers; -use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Place, Local}; +use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place}; use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; use transform::nll; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_set::{self, IdxSetBuf}; -use rustc_data_structures::indexed_vec::{Idx}; +use rustc_data_structures::indexed_vec::Idx; -use syntax::ast::{self}; +use syntax::ast; use syntax_pos::Span; -use dataflow::{do_dataflow}; -use dataflow::{MoveDataParamEnv}; +use dataflow::do_dataflow; +use dataflow::MoveDataParamEnv; use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; -use dataflow::{MovingOutStatements, EverInitializedLvals}; -use dataflow::{Borrows, BorrowData, BorrowIndex}; -use dataflow::move_paths::{MoveError, IllegalMoveOriginKind}; -use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult, MoveOutIndex}; +use dataflow::{EverInitializedLvals, MovingOutStatements}; +use dataflow::{BorrowData, BorrowIndex, Borrows}; +use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; +use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex}; use util::borrowck_errors::{BorrowckErrors, Origin}; use self::MutateMode::{JustWrite, WriteAndRead}; @@ -52,9 +52,8 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); if { - !tcx.has_attr(def_id, "rustc_mir_borrowck") && - !tcx.sess.opts.borrowck_mode.use_mir() && - !tcx.sess.opts.debugging_opts.nll + !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir() + && !tcx.sess.opts.debugging_opts.nll } { return; } @@ -66,14 +65,16 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { debug!("mir_borrowck done"); } -fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - input_mir: &Mir<'gcx>, - def_id: DefId) -{ +fn do_mir_borrowck<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + input_mir: &Mir<'gcx>, + def_id: DefId, +) { let tcx = infcx.tcx; let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - let id = tcx.hir.as_local_node_id(def_id) + let id = tcx.hir + .as_local_node_id(def_id) .expect("do_mir_borrowck: non-local DefId"); let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx) { @@ -81,20 +82,27 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, Err((move_data, move_errors)) => { for move_error in move_errors { let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { - MoveError::UnionMove { .. } => - unimplemented!("dont know how to report union move errors yet."), - MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind), + MoveError::UnionMove { .. } => { + unimplemented!("dont know how to report union move errors yet.") + } + MoveError::IllegalMove { + cannot_move_out_of: o, + } => (o.span, o.kind), }; let origin = Origin::Mir; let mut err = match kind { - IllegalMoveOriginKind::Static => - tcx.cannot_move_out_of(span, "static item", origin), - IllegalMoveOriginKind::BorrowedContent => - tcx.cannot_move_out_of(span, "borrowed content", origin), - IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => - tcx.cannot_move_out_of_interior_of_drop(span, ty, origin), - IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => - tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), + IllegalMoveOriginKind::Static => { + tcx.cannot_move_out_of(span, "static item", origin) + } + IllegalMoveOriginKind::BorrowedContent => { + tcx.cannot_move_out_of(span, "borrowed content", origin) + } + IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { + tcx.cannot_move_out_of_interior_of_drop(span, ty, origin) + } + IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin) + } }; err.emit(); } @@ -115,23 +123,56 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, Some(nll::compute_regions(infcx, def_id, param_env, mir)) }; - let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; + let mdpe = MoveDataParamEnv { + move_data: move_data, + param_env: param_env, + }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir, opt_regioncx.as_ref()), - |bd, i| bd.location(i)); - let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeInitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeUninitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - let flow_move_outs = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MovingOutStatements::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().moves[i]); - let flow_ever_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - EverInitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().inits[i]); + let flow_borrows = do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + Borrows::new(tcx, mir, opt_regioncx.as_ref()), + |bd, i| bd.location(i), + ); + let flow_inits = do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MaybeInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i], + ); + let flow_uninits = do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MaybeUninitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i], + ); + let flow_move_outs = do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MovingOutStatements::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().moves[i], + ); + let flow_ever_inits = do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + EverInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().inits[i], + ); let mut mbcx = MirBorrowckCtxt { tcx: tcx, @@ -142,11 +183,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, storage_dead_or_drop_error_reported: FxHashSet(), }; - let mut state = InProgress::new(flow_borrows, - flow_inits, - flow_uninits, - flow_move_outs, - flow_ever_inits); + let mut state = InProgress::new( + flow_borrows, + flow_inits, + flow_uninits, + flow_move_outs, + flow_ever_inits, + ); mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer } @@ -173,7 +216,10 @@ pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { ever_inits: FlowInProgress>, } -struct FlowInProgress where BD: BitDenotation { +struct FlowInProgress +where + BD: BitDenotation, +{ base_results: DataflowResults, curr_state: IdxSetBuf, stmt_gen: IdxSetBuf, @@ -188,59 +234,76 @@ struct FlowInProgress where BD: BitDenotation { impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> { type FlowState = InProgress<'cx, 'gcx, 'tcx>; - fn mir(&self) -> &'cx Mir<'tcx> { self.mir } + fn mir(&self) -> &'cx Mir<'tcx> { + self.mir + } fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reset_to_entry_of(bb), - |i| i.reset_to_entry_of(bb), - |u| u.reset_to_entry_of(bb), - |m| m.reset_to_entry_of(bb), - |e| e.reset_to_entry_of(bb)); + flow_state.each_flow( + |b| b.reset_to_entry_of(bb), + |i| i.reset_to_entry_of(bb), + |u| u.reset_to_entry_of(bb), + |m| m.reset_to_entry_of(bb), + |e| e.reset_to_entry_of(bb), + ); } - fn reconstruct_statement_effect(&mut self, - location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reconstruct_statement_effect(location), - |i| i.reconstruct_statement_effect(location), - |u| u.reconstruct_statement_effect(location), - |m| m.reconstruct_statement_effect(location), - |e| e.reconstruct_statement_effect(location)); + fn reconstruct_statement_effect( + &mut self, + location: Location, + flow_state: &mut Self::FlowState, + ) { + flow_state.each_flow( + |b| b.reconstruct_statement_effect(location), + |i| i.reconstruct_statement_effect(location), + |u| u.reconstruct_statement_effect(location), + |m| m.reconstruct_statement_effect(location), + |e| e.reconstruct_statement_effect(location), + ); } - fn apply_local_effect(&mut self, - _location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.apply_local_effect(), - |i| i.apply_local_effect(), - |u| u.apply_local_effect(), - |m| m.apply_local_effect(), - |e| e.apply_local_effect()); + fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) { + flow_state.each_flow( + |b| b.apply_local_effect(), + |i| i.apply_local_effect(), + |u| u.apply_local_effect(), + |m| m.apply_local_effect(), + |e| e.apply_local_effect(), + ); } - fn reconstruct_terminator_effect(&mut self, - location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reconstruct_terminator_effect(location), - |i| i.reconstruct_terminator_effect(location), - |u| u.reconstruct_terminator_effect(location), - |m| m.reconstruct_terminator_effect(location), - |e| e.reconstruct_terminator_effect(location)); + fn reconstruct_terminator_effect( + &mut self, + location: Location, + flow_state: &mut Self::FlowState, + ) { + flow_state.each_flow( + |b| b.reconstruct_terminator_effect(location), + |i| i.reconstruct_terminator_effect(location), + |u| u.reconstruct_terminator_effect(location), + |m| m.reconstruct_terminator_effect(location), + |e| e.reconstruct_terminator_effect(location), + ); } - fn visit_block_entry(&mut self, - bb: BasicBlock, - flow_state: &Self::FlowState) { + fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) { let summary = flow_state.summary(); debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary); } - fn visit_statement_entry(&mut self, - location: Location, - stmt: &Statement<'tcx>, - flow_state: &Self::FlowState) { + fn visit_statement_entry( + &mut self, + location: Location, + stmt: &Statement<'tcx>, + flow_state: &Self::FlowState, + ) { let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary); + debug!( + "MirBorrowckCtxt::process_statement({:?}, {:?}): {}", + location, + stmt, + summary + ); let span = stmt.source_info.span; match stmt.kind { StatementKind::Assign(ref lhs, ref rhs) => { @@ -249,38 +312,64 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // to shallow requires to dataflow: "if this is an // assignment `place = `, then any loan for some // path P of which `place` is a prefix is killed." - self.mutate_place(ContextKind::AssignLhs.new(location), - (lhs, span), Deep, JustWrite, flow_state); - - self.consume_rvalue(ContextKind::AssignRhs.new(location), - (rhs, span), location, flow_state); + self.mutate_place( + ContextKind::AssignLhs.new(location), + (lhs, span), + Deep, + JustWrite, + flow_state, + ); + + self.consume_rvalue( + ContextKind::AssignRhs.new(location), + (rhs, span), + location, + flow_state, + ); } - StatementKind::SetDiscriminant { ref place, variant_index: _ } => { - self.mutate_place(ContextKind::SetDiscrim.new(location), - (place, span), - Shallow(Some(ArtificialField::Discriminant)), - JustWrite, - flow_state); + StatementKind::SetDiscriminant { + ref place, + variant_index: _, + } => { + self.mutate_place( + ContextKind::SetDiscrim.new(location), + (place, span), + Shallow(Some(ArtificialField::Discriminant)), + JustWrite, + flow_state, + ); } - StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { + StatementKind::InlineAsm { + ref asm, + ref outputs, + ref inputs, + } => { let context = ContextKind::InlineAsm.new(location); for (o, output) in asm.outputs.iter().zip(outputs) { if o.is_indirect { // FIXME(eddyb) indirect inline asm outputs should // be encoeded through MIR place derefs instead. - self.access_place(context, - (output, span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state); - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (output, span), flow_state); + self.access_place( + context, + (output, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (output, span), + flow_state, + ); } else { - self.mutate_place(context, - (output, span), - Deep, - if o.is_rw { WriteAndRead } else { JustWrite }, - flow_state); + self.mutate_place( + context, + (output, span), + Deep, + if o.is_rw { WriteAndRead } else { JustWrite }, + flow_state, + ); } } for input in inputs { @@ -291,114 +380,158 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // ignored when consuming results (update to // flow_state already handled). } - StatementKind::Nop | - StatementKind::Validate(..) | - StatementKind::StorageLive(..) => { + StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { // `Nop`, `Validate`, and `StorageLive` are irrelevant // to borrow check. } StatementKind::StorageDead(local) => { - self.access_place(ContextKind::StorageDead.new(location), + self.access_place( + ContextKind::StorageDead.new(location), (&Place::Local(local), span), (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, - flow_state); + flow_state, + ); } } } - fn visit_terminator_entry(&mut self, - location: Location, - term: &Terminator<'tcx>, - flow_state: &Self::FlowState) { + fn visit_terminator_entry( + &mut self, + location: Location, + term: &Terminator<'tcx>, + flow_state: &Self::FlowState, + ) { let loc = location; let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location, term, summary); + debug!( + "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", + location, + term, + summary + ); let span = term.source_info.span; match term.kind { - TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { - self.consume_operand(ContextKind::SwitchInt.new(loc), - (discr, span), flow_state); + TerminatorKind::SwitchInt { + ref discr, + switch_ty: _, + values: _, + targets: _, + } => { + self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state); } - TerminatorKind::Drop { location: ref drop_place, target: _, unwind: _ } => { - self.access_place(ContextKind::Drop.new(loc), - (drop_place, span), - (Deep, Write(WriteKind::StorageDeadOrDrop)), - LocalMutationIsAllowed::Yes, - flow_state); + TerminatorKind::Drop { + location: ref drop_place, + target: _, + unwind: _, + } => { + self.access_place( + ContextKind::Drop.new(loc), + (drop_place, span), + (Deep, Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + flow_state, + ); } - TerminatorKind::DropAndReplace { location: ref drop_place, - value: ref new_value, - target: _, - unwind: _ } => { - self.mutate_place(ContextKind::DropAndReplace.new(loc), - (drop_place, span), - Deep, - JustWrite, - flow_state); - self.consume_operand(ContextKind::DropAndReplace.new(loc), - (new_value, span), flow_state); + TerminatorKind::DropAndReplace { + location: ref drop_place, + value: ref new_value, + target: _, + unwind: _, + } => { + self.mutate_place( + ContextKind::DropAndReplace.new(loc), + (drop_place, span), + Deep, + JustWrite, + flow_state, + ); + self.consume_operand( + ContextKind::DropAndReplace.new(loc), + (new_value, span), + flow_state, + ); } - TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - self.consume_operand(ContextKind::CallOperator.new(loc), - (func, span), flow_state); + TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup: _, + } => { + self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state); for arg in args { - self.consume_operand(ContextKind::CallOperand.new(loc), - (arg, span), flow_state); + self.consume_operand( + ContextKind::CallOperand.new(loc), + (arg, span), + flow_state, + ); } - if let Some((ref dest, _/*bb*/)) = *destination { - self.mutate_place(ContextKind::CallDest.new(loc), - (dest, span), - Deep, - JustWrite, - flow_state); + if let Some((ref dest, _ /*bb*/)) = *destination { + self.mutate_place( + ContextKind::CallDest.new(loc), + (dest, span), + Deep, + JustWrite, + flow_state, + ); } } - TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { - self.consume_operand(ContextKind::Assert.new(loc), - (cond, span), flow_state); + TerminatorKind::Assert { + ref cond, + expected: _, + ref msg, + target: _, + cleanup: _, + } => { + self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state); match *msg { AssertMessage::BoundsCheck { ref len, ref index } => { - self.consume_operand(ContextKind::Assert.new(loc), - (len, span), flow_state); - self.consume_operand(ContextKind::Assert.new(loc), - (index, span), flow_state); + self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state); + self.consume_operand( + ContextKind::Assert.new(loc), + (index, span), + flow_state, + ); } - AssertMessage::Math(_/*const_math_err*/) => {} + AssertMessage::Math(_ /*const_math_err*/) => {} AssertMessage::GeneratorResumedAfterReturn => {} AssertMessage::GeneratorResumedAfterPanic => {} } } - TerminatorKind::Yield { ref value, resume: _, drop: _} => { - self.consume_operand(ContextKind::Yield.new(loc), - (value, span), flow_state); + TerminatorKind::Yield { + ref value, + resume: _, + drop: _, + } => { + self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state); } - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop => { + TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Returning from the function implicitly kills storage for all locals and statics. // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), // so this "extra check" serves as a kind of backup. let domain = flow_state.borrows.base_results.operator(); let data = domain.borrows(); - flow_state.borrows.with_elems_outgoing(|borrows| for i in borrows { - let borrow = &data[i]; - - if self.place_is_invalidated_at_exit(&borrow.place) { - debug!("borrow conflicts at exit {:?}", borrow); - let borrow_span = self.mir.source_info(borrow.location).span; - // FIXME: should be talking about the region lifetime instead - // of just a span here. - let end_span = domain.opt_region_end_span(&borrow.region); - - self.report_borrowed_value_does_not_live_long_enough( - ContextKind::StorageDead.new(loc), - (&borrow.place, borrow_span), - end_span) + flow_state.borrows.with_elems_outgoing(|borrows| { + for i in borrows { + let borrow = &data[i]; + + if self.place_is_invalidated_at_exit(&borrow.place) { + debug!("borrow conflicts at exit {:?}", borrow); + let borrow_span = self.mir.source_info(borrow.location).span; + // FIXME: should be talking about the region lifetime instead + // of just a span here. + let end_span = domain.opt_region_end_span(&borrow.region); + + self.report_borrowed_value_does_not_live_long_enough( + ContextKind::StorageDead.new(loc), + (&borrow.place, borrow_span), + end_span, + ) + } } }); } @@ -412,12 +545,18 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum MutateMode { JustWrite, WriteAndRead } +enum MutateMode { + JustWrite, + WriteAndRead, +} #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum Control { Continue, Break } +enum Control { + Continue, + Break, +} -use self::ShallowOrDeep::{Shallow, Deep}; +use self::ShallowOrDeep::{Deep, Shallow}; use self::ReadOrWrite::{Read, Write}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -484,7 +623,7 @@ enum WriteKind { #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum LocalMutationIsAllowed { Yes, - No + No, } #[derive(Copy, Clone)] @@ -498,19 +637,19 @@ enum InitializationRequiringAction { impl InitializationRequiringAction { fn as_noun(self) -> &'static str { match self { - InitializationRequiringAction::Update => "update", - InitializationRequiringAction::Borrow => "borrow", - InitializationRequiringAction::Use => "use", - InitializationRequiringAction::Assignment => "assign" + InitializationRequiringAction::Update => "update", + InitializationRequiringAction::Borrow => "borrow", + InitializationRequiringAction::Use => "use", + InitializationRequiringAction::Assignment => "assign", } } fn as_verb_in_past_tense(self) -> &'static str { match self { - InitializationRequiringAction::Update => "updated", - InitializationRequiringAction::Borrow => "borrowed", - InitializationRequiringAction::Use => "used", - InitializationRequiringAction::Assignment => "assigned" + InitializationRequiringAction::Update => "updated", + InitializationRequiringAction::Borrow => "borrowed", + InitializationRequiringAction::Use => "used", + InitializationRequiringAction::Assignment => "assigned", } } } @@ -522,17 +661,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// access. /// /// Returns true if an error is reported, false otherwise. - fn access_place(&mut self, - context: Context, - place_span: (&Place<'tcx>, Span), - kind: (ShallowOrDeep, ReadOrWrite), - is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn access_place( + &mut self, + context: Context, + place_span: (&Place<'tcx>, Span), + kind: (ShallowOrDeep, ReadOrWrite), + is_local_mutation_allowed: LocalMutationIsAllowed, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { let (sd, rw) = kind; let storage_dead_or_drop_local = match (place_span.0, rw) { (&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local), - _ => None + _ => None, }; // Check if error has already been reported to stop duplicate reporting. @@ -543,70 +684,84 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } // Check permissions - let mut error_reported = self.check_access_permissions(place_span, - rw, - is_local_mutation_allowed); + let mut error_reported = + self.check_access_permissions(place_span, rw, is_local_mutation_allowed); self.each_borrow_involving_path( - context, (sd, place_span.0), flow_state, |this, _index, borrow, common_prefix| { - match (rw, borrow.kind) { - (Read(_), BorrowKind::Shared) => { - Control::Continue - } - (Read(kind), BorrowKind::Unique) | - (Read(kind), BorrowKind::Mut) => { - match kind { - ReadKind::Copy => { - error_reported = true; - this.report_use_while_mutably_borrowed( - context, place_span, borrow) - }, - ReadKind::Borrow(bk) => { - let end_issued_loan_span = - flow_state.borrows.base_results.operator().opt_region_end_span( - &borrow.region); - error_reported = true; - this.report_conflicting_borrow( - context, common_prefix, place_span, bk, - &borrow, end_issued_loan_span) - } + context, + (sd, place_span.0), + flow_state, + |this, _index, borrow, common_prefix| match (rw, borrow.kind) { + (Read(_), BorrowKind::Shared) => Control::Continue, + (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => { + match kind { + ReadKind::Copy => { + error_reported = true; + this.report_use_while_mutably_borrowed(context, place_span, borrow) + } + ReadKind::Borrow(bk) => { + let end_issued_loan_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_conflicting_borrow( + context, + common_prefix, + place_span, + bk, + &borrow, + end_issued_loan_span, + ) } - Control::Break } - (Write(kind), _) => { - match kind { - WriteKind::MutableBorrow(bk) => { - let end_issued_loan_span = - flow_state.borrows.base_results.operator().opt_region_end_span( - &borrow.region); - error_reported = true; - this.report_conflicting_borrow( - context, common_prefix, place_span, bk, - &borrow, end_issued_loan_span) - } - WriteKind::StorageDeadOrDrop => { - let end_span = - flow_state.borrows.base_results.operator().opt_region_end_span( - &borrow.region); - error_reported = true; - this.report_borrowed_value_does_not_live_long_enough( - context, place_span, end_span) - }, - WriteKind::Mutate => { - error_reported = true; - this.report_illegal_mutation_of_borrowed( - context, place_span, borrow) - }, - WriteKind::Move => { - error_reported = true; - this.report_move_out_while_borrowed( - context, place_span, &borrow) - }, + Control::Break + } + (Write(kind), _) => { + match kind { + WriteKind::MutableBorrow(bk) => { + let end_issued_loan_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_conflicting_borrow( + context, + common_prefix, + place_span, + bk, + &borrow, + end_issued_loan_span, + ) + } + WriteKind::StorageDeadOrDrop => { + let end_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_borrowed_value_does_not_live_long_enough( + context, + place_span, + end_span, + ) + } + WriteKind::Mutate => { + error_reported = true; + this.report_illegal_mutation_of_borrowed(context, place_span, borrow) + } + WriteKind::Move => { + error_reported = true; + this.report_move_out_while_borrowed(context, place_span, &borrow) } - Control::Break } + Control::Break } - }); + }, + ); if error_reported { if let Some(local) = storage_dead_or_drop_local { @@ -615,75 +770,97 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn mutate_place(&mut self, - context: Context, - place_span: (&Place<'tcx>, Span), - kind: ShallowOrDeep, - mode: MutateMode, - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn mutate_place( + &mut self, + context: Context, + place_span: (&Place<'tcx>, Span), + kind: ShallowOrDeep, + mode: MutateMode, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. match mode { MutateMode::WriteAndRead => { - self.check_if_path_is_moved(context, InitializationRequiringAction::Update, - place_span, flow_state); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Update, + place_span, + flow_state, + ); } MutateMode::JustWrite => { self.check_if_assigned_path_is_moved(context, place_span, flow_state); } } - self.access_place(context, - place_span, - (kind, Write(WriteKind::Mutate)), - LocalMutationIsAllowed::Yes, - flow_state); + self.access_place( + context, + place_span, + (kind, Write(WriteKind::Mutate)), + LocalMutationIsAllowed::Yes, + flow_state, + ); // check for reassignments to immutable local variables self.check_if_reassignment_to_immutable_state(context, place_span, flow_state); } - fn consume_rvalue(&mut self, - context: Context, - (rvalue, span): (&Rvalue<'tcx>, Span), - _location: Location, - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn consume_rvalue( + &mut self, + context: Context, + (rvalue, span): (&Rvalue<'tcx>, Span), + _location: Location, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { match *rvalue { - Rvalue::Ref(_/*rgn*/, bk, ref place) => { + Rvalue::Ref(_ /*rgn*/, bk, ref place) => { let access_kind = match bk { BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), - BorrowKind::Unique | - BorrowKind::Mut => (Deep, Write(WriteKind::MutableBorrow(bk))), + BorrowKind::Unique | BorrowKind::Mut => { + (Deep, Write(WriteKind::MutableBorrow(bk))) + } }; - self.access_place(context, - (place, span), - access_kind, - LocalMutationIsAllowed::No, - flow_state); - self.check_if_path_is_moved(context, InitializationRequiringAction::Borrow, - (place, span), flow_state); + self.access_place( + context, + (place, span), + access_kind, + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Borrow, + (place, span), + flow_state, + ); } Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | - Rvalue::UnaryOp(_/*un_op*/, ref operand) | - Rvalue::Cast(_/*cast_kind*/, ref operand, _/*ty*/) => { + Rvalue::UnaryOp(_ /*un_op*/, ref operand) | + Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { self.consume_operand(context, (operand, span), flow_state) } - Rvalue::Len(ref place) | - Rvalue::Discriminant(ref place) => { + Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => { let af = match *rvalue { Rvalue::Len(..) => ArtificialField::ArrayLength, Rvalue::Discriminant(..) => ArtificialField::Discriminant, _ => unreachable!(), }; - self.access_place(context, - (place, span), - (Shallow(Some(af)), Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state); - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (place, span), flow_state); + self.access_place( + context, + (place, span), + (Shallow(Some(af)), Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); } Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) | @@ -700,43 +877,55 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `NullOp::Box`? } - Rvalue::Aggregate(ref _aggregate_kind, ref operands) => { - for operand in operands { - self.consume_operand(context, (operand, span), flow_state); - } - } + Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands { + self.consume_operand(context, (operand, span), flow_state); + }, } } - fn consume_operand(&mut self, - context: Context, - (operand, span): (&Operand<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn consume_operand( + &mut self, + context: Context, + (operand, span): (&Operand<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { match *operand { Operand::Copy(ref place) => { // copy of place: check if this is "copy of frozen path" // (FIXME: see check_loans.rs) - self.access_place(context, - (place, span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state); + self.access_place( + context, + (place, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); // Finally, check if path was already moved. - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (place, span), flow_state); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); } Operand::Move(ref place) => { // move of place: check if this is move of already borrowed path - self.access_place(context, - (place, span), - (Deep, Write(WriteKind::Move)), - LocalMutationIsAllowed::Yes, - flow_state); + self.access_place( + context, + (place, span), + (Deep, Write(WriteKind::Move)), + LocalMutationIsAllowed::Yes, + flow_state, + ); // Finally, check if path was already moved. - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (place, span), flow_state); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); } Operand::Constant(_) => {} } @@ -755,9 +944,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Static(statik) => { // Thread-locals might be dropped after the function exits, but // "true" statics will never be. - let is_thread_local = self.tcx.get_attrs(statik.def_id).iter().any(|attr| { - attr.check_name("thread_local") - }); + let is_thread_local = self.tcx + .get_attrs(statik.def_id) + .iter() + .any(|attr| attr.check_name("thread_local")); (true, is_thread_local) } @@ -766,12 +956,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // have a destructor it would've been called already. (false, true) } - Place::Projection(..) => bug!("root of {:?} is a projection ({:?})?", - place, root_place) + Place::Projection(..) => { + bug!("root of {:?} is a projection ({:?})?", place, root_place) + } }; if !will_be_dropped { - debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place); + debug!( + "place_is_invalidated_at_exit({:?}) - won't be dropped", + place + ); return false; } @@ -783,15 +977,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { PrefixSet::Shallow }; - self.prefixes(place, prefix_set).any(|prefix| prefix == root_place) + self.prefixes(place, prefix_set) + .any(|prefix| prefix == root_place) } } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn check_if_reassignment_to_immutable_state(&mut self, - context: Context, - (place, span): (&Place<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn check_if_reassignment_to_immutable_state( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { let move_data = self.move_data; // determine if this path has a non-mut owner (and thus needs checking). @@ -804,33 +1001,37 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } match self.move_path_closest_to(place) { - Ok(mpi) => { - for ii in &move_data.init_path_map[mpi] { - if flow_state.ever_inits.curr_state.contains(ii) { - let first_assign_span = self.move_data.inits[*ii].span; - self.report_illegal_reassignment( - context, (place, span), first_assign_span); - break; - } + Ok(mpi) => for ii in &move_data.init_path_map[mpi] { + if flow_state.ever_inits.curr_state.contains(ii) { + let first_assign_span = self.move_data.inits[*ii].span; + self.report_illegal_reassignment(context, (place, span), first_assign_span); + break; } }, Err(NoMovePathFound::ReachedStatic) => { let item_msg = match self.describe_place(place) { Some(name) => format!("immutable static item `{}`", name), - None => "immutable static item".to_owned() + None => "immutable static item".to_owned(), }; - self.tcx.sess.delay_span_bug(span, - &format!("cannot assign to {}, should have been caught by \ - `check_access_permissions()`", item_msg)); - }, + self.tcx.sess.delay_span_bug( + span, + &format!( + "cannot assign to {}, should have been caught by \ + `check_access_permissions()`", + item_msg + ), + ); + } } } - fn check_if_path_is_moved(&mut self, - context: Context, - desired_action: InitializationRequiringAction, - place_span: (&Place<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn check_if_path_is_moved( + &mut self, + context: Context, + desired_action: InitializationRequiringAction, + place_span: (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { // FIXME: analogous code in check_loans first maps `place` to // its base_path ... but is that what we want here? let place = self.base_path(place_span.0); @@ -878,22 +1079,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { match self.move_path_closest_to(place) { Ok(mpi) => { if maybe_uninits.curr_state.contains(&mpi) { - self.report_use_of_moved_or_uninitialized(context, desired_action, - place_span, mpi, - curr_move_outs); + self.report_use_of_moved_or_uninitialized( + context, + desired_action, + place_span, + mpi, + curr_move_outs, + ); return; // don't bother finding other problems. } } Err(NoMovePathFound::ReachedStatic) => { // Okay: we do not build MoveData for static variables - } - - // Only query longest prefix with a MovePath, not further - // ancestors; dataflow recurs on children when parents - // move (to support partial (re)inits). - // - // (I.e. querying parents breaks scenario 8; but may want - // to do such a query based on partial-init feature-gate.) + } // Only query longest prefix with a MovePath, not further + // ancestors; dataflow recurs on children when parents + // move (to support partial (re)inits). + // + // (I.e. querying parents breaks scenario 8; but may want + // to do such a query based on partial-init feature-gate.) } // A move of any shallow suffix of `place` also interferes @@ -906,9 +1109,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { debug!("check_if_path_is_moved part2 place: {:?}", place); if let Some(mpi) = self.move_path_for_place(place) { if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) { - self.report_use_of_moved_or_uninitialized(context, desired_action, - place_span, child_mpi, - curr_move_outs); + self.report_use_of_moved_or_uninitialized( + context, + desired_action, + place_span, + child_mpi, + curr_move_outs, + ); return; // don't bother finding other problems. } } @@ -924,9 +1131,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// An Err result includes a tag indicated why the search failed. /// Currenly this can only occur if the place is built off of a /// static variable, as we do not track those in the MoveData. - fn move_path_closest_to(&mut self, place: &Place<'tcx>) - -> Result - { + fn move_path_closest_to( + &mut self, + place: &Place<'tcx>, + ) -> Result { let mut last_prefix = place; for prefix in self.prefixes(place, PrefixSet::All) { if let Some(mpi) = self.move_path_for_place(prefix) { @@ -941,10 +1149,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn move_path_for_place(&mut self, - place: &Place<'tcx>) - -> Option - { + fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option { // If returns None, then there is no move path corresponding // to a direct owner of `place` (which means there is nothing // that borrowck tracks for its analysis). @@ -955,10 +1160,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn check_if_assigned_path_is_moved(&mut self, - context: Context, - (place, span): (&Place<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn check_if_assigned_path_is_moved( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { // recur down place; dispatch to check_if_path_is_moved when necessary let mut place = place; loop { @@ -1021,42 +1228,46 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// Check the permissions for the given place and read or write kind /// /// Returns true if an error is reported, false otherwise. - fn check_access_permissions(&self, - (place, span): (&Place<'tcx>, Span), - kind: ReadOrWrite, - is_local_mutation_allowed: LocalMutationIsAllowed) - -> bool { - debug!("check_access_permissions({:?}, {:?}, {:?})", - place, kind, is_local_mutation_allowed); + fn check_access_permissions( + &self, + (place, span): (&Place<'tcx>, Span), + kind: ReadOrWrite, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> bool { + debug!( + "check_access_permissions({:?}, {:?}, {:?})", + place, + kind, + is_local_mutation_allowed + ); let mut error_reported = false; match kind { Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { if let Err(_place_err) = self.is_unique(place) { span_bug!(span, "&unique borrow for {:?} should not fail", place); } - }, - Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => { - if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { - error_reported = true; + } + Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) = + self.is_mutable(place, is_local_mutation_allowed) + { + error_reported = true; - let item_msg = match self.describe_place(place) { - Some(name) => format!("immutable item `{}`", name), - None => "immutable item".to_owned() - }; + let item_msg = match self.describe_place(place) { + Some(name) => format!("immutable item `{}`", name), + None => "immutable item".to_owned(), + }; - let mut err = self.tcx.cannot_borrow_path_as_mutable(span, - &item_msg, - Origin::Mir); - err.span_label(span, "cannot borrow as mutable"); + let mut err = self.tcx + .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir); + err.span_label(span, "cannot borrow as mutable"); - if place != place_err { - if let Some(name) = self.describe_place(place_err) { - err.note(&format!("Value not mutable causing this error: `{}`", name)); - } + if place != place_err { + if let Some(name) = self.describe_place(place_err) { + err.note(&format!("Value not mutable causing this error: `{}`", name)); } - - err.emit(); } + + err.emit(); }, Write(WriteKind::Mutate) => { if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { @@ -1064,12 +1275,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let item_msg = match self.describe_place(place) { Some(name) => format!("immutable item `{}`", name), - None => "immutable item".to_owned() + None => "immutable item".to_owned(), }; - let mut err = self.tcx.cannot_assign(span, - &item_msg, - Origin::Mir); + let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir); err.span_label(span, "cannot mutate"); if place != place_err { @@ -1080,17 +1289,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - }, + } Write(WriteKind::Move) | Write(WriteKind::StorageDeadOrDrop) | Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { - self.tcx.sess.delay_span_bug(span, - &format!("Accessing `{:?}` with the kind `{:?}` shouldn't be possible", + self.tcx.sess.delay_span_bug( + span, + &format!( + "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", place, - kind)); + kind + ), + ); } - }, + } Read(ReadKind::Borrow(BorrowKind::Unique)) | Read(ReadKind::Borrow(BorrowKind::Mut)) | Read(ReadKind::Borrow(BorrowKind::Shared)) | @@ -1101,28 +1314,26 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } /// Can this value be written or borrowed mutably - fn is_mutable<'d>(&self, - place: &'d Place<'tcx>, - is_local_mutation_allowed: LocalMutationIsAllowed) - -> Result<(), &'d Place<'tcx>> { + fn is_mutable<'d>( + &self, + place: &'d Place<'tcx>, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> Result<(), &'d Place<'tcx>> { match *place { Place::Local(local) => { let local = &self.mir.local_decls[local]; match local.mutability { - Mutability::Not => - match is_local_mutation_allowed { - LocalMutationIsAllowed::Yes => Ok(()), - LocalMutationIsAllowed::No => Err(place), - }, - Mutability::Mut => Ok(()) - } - }, - Place::Static(ref static_) => { - if !self.tcx.is_static_mut(static_.def_id) { - Err(place) - } else { - Ok(()) + Mutability::Not => match is_local_mutation_allowed { + LocalMutationIsAllowed::Yes => Ok(()), + LocalMutationIsAllowed::No => Err(place), + }, + Mutability::Mut => Ok(()), } + } + Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) { + Err(place) + } else { + Ok(()) }, Place::Projection(ref proj) => { match proj.elem { @@ -1143,9 +1354,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } else { self.is_unique(&proj.base) } - }, + } } - }, + } ty::TyRawPtr(tnm) => { match tnm.mutbl { // `*const` raw pointers are not mutable @@ -1154,20 +1365,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // The users have to check by themselve. hir::MutMutable => Ok(()), } - }, + } // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => - self.is_mutable(&proj.base, LocalMutationIsAllowed::No), + _ if base_ty.is_box() => { + self.is_mutable(&proj.base, LocalMutationIsAllowed::No) + } // Deref should only be for reference, pointers or boxes _ => bug!("Deref of unexpected type: {:?}", base_ty), } - }, + } // All other projections are owned by their base path, so mutable if // base path is mutable ProjectionElem::Field(..) | ProjectionElem::Index(..) | - ProjectionElem::ConstantIndex{..} | - ProjectionElem::Subslice{..} | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..) => { let field_projection = self.is_upvar_field_projection(place); @@ -1193,11 +1405,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Local(..) => { // Local variables are unique Ok(()) - }, + } Place::Static(..) => { // Static variables are not Err(place) - }, + } Place::Projection(ref proj) => { match proj.elem { ProjectionElem::Deref => { @@ -1217,7 +1429,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `&mut T` is as unique as the context in which it is found hir::MutMutable => self.is_unique(&proj.base), } - }, + } ty::TyRawPtr(tnm) => { match tnm.mutbl { // `*mut` can be aliased, but we leave it to user @@ -1225,18 +1437,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `*const` is treated the same as `*mut` hir::MutImmutable => Ok(()), } - }, + } // Deref should only be for reference, pointers or boxes - _ => bug!("Deref of unexpected type: {:?}", base_ty) + _ => bug!("Deref of unexpected type: {:?}", base_ty), } - }, + } // Other projections are unique if the base is unique ProjectionElem::Field(..) | ProjectionElem::Index(..) | - ProjectionElem::ConstantIndex{..} | - ProjectionElem::Subslice{..} | - ProjectionElem::Downcast(..) => - self.is_unique(&proj.base) + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | + ProjectionElem::Downcast(..) => self.is_unique(&proj.base), } } } @@ -1249,12 +1460,14 @@ enum NoMovePathFound { } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn each_borrow_involving_path(&mut self, - _context: Context, - access_place: (ShallowOrDeep, &Place<'tcx>), - flow_state: &InProgress<'cx, 'gcx, 'tcx>, - mut op: F) - where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control + fn each_borrow_involving_path( + &mut self, + _context: Context, + access_place: (ShallowOrDeep, &Place<'tcx>), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + mut op: F, + ) where + F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control, { let (access, place) = access_place; @@ -1278,7 +1491,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if *accessed_prefix == borrowed.place { // FIXME: pass in enum describing case we are in? let ctrl = op(self, i, borrowed, accessed_prefix); - if ctrl == Control::Break { return; } + if ctrl == Control::Break { + return; + } } } @@ -1304,7 +1519,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if borrowed_prefix == place { // FIXME: pass in enum describing case we are in? let ctrl = op(self, i, borrowed, borrowed_prefix); - if ctrl == Control::Break { return; } + if ctrl == Control::Break { + return; + } } } } @@ -1322,11 +1539,11 @@ use self::prefixes::PrefixSet; /// is borrowed. But: writing `a` is legal if `*a` is borrowed, /// whether or not `a` is a shared or mutable reference. [...] " mod prefixes { - use super::{MirBorrowckCtxt}; + use super::MirBorrowckCtxt; use rustc::hir; use rustc::ty::{self, TyCtxt}; - use rustc::mir::{Place, Mir, ProjectionElem}; + use rustc::mir::{Mir, Place, ProjectionElem}; pub trait IsPrefixOf<'tcx> { fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; @@ -1341,8 +1558,7 @@ mod prefixes { } match *cursor { - Place::Local(_) | - Place::Static(_) => return false, + Place::Local(_) | Place::Static(_) => return false, Place::Projection(ref proj) => { cursor = &proj.base; } @@ -1374,12 +1590,17 @@ mod prefixes { /// Returns an iterator over the prefixes of `place` /// (inclusive) from longest to smallest, potentially /// terminating the iteration early based on `kind`. - pub(super) fn prefixes(&self, - place: &'cx Place<'tcx>, - kind: PrefixSet) - -> Prefixes<'cx, 'gcx, 'tcx> - { - Prefixes { next: Some(place), kind, mir: self.mir, tcx: self.tcx } + pub(super) fn prefixes( + &self, + place: &'cx Place<'tcx>, + kind: PrefixSet, + ) -> Prefixes<'cx, 'gcx, 'tcx> { + Prefixes { + next: Some(place), + kind, + mir: self.mir, + tcx: self.tcx, + } } } @@ -1408,7 +1629,7 @@ mod prefixes { }; match proj.elem { - ProjectionElem::Field(_/*field*/, _/*ty*/) => { + ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { // FIXME: add union handling self.next = Some(&proj.base); return Some(cursor); @@ -1454,13 +1675,25 @@ mod prefixes { let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); match ty.sty { ty::TyRawPtr(_) | - ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => { + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutImmutable, + }, + ) => { // don't continue traversing over derefs of raw pointers or shared borrows. self.next = None; return Some(cursor); } - ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => { + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutMutable, + }, + ) => { self.next = Some(&proj.base); return Some(cursor); } @@ -1478,47 +1711,59 @@ mod prefixes { } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn report_use_of_moved_or_uninitialized(&mut self, - _context: Context, - desired_action: InitializationRequiringAction, - (place, span): (&Place<'tcx>, Span), - mpi: MovePathIndex, - curr_move_out: &IdxSetBuf) { - - let mois = self.move_data.path_map[mpi].iter().filter( - |moi| curr_move_out.contains(moi)).collect::>(); + fn report_use_of_moved_or_uninitialized( + &mut self, + _context: Context, + desired_action: InitializationRequiringAction, + (place, span): (&Place<'tcx>, Span), + mpi: MovePathIndex, + curr_move_out: &IdxSetBuf, + ) { + let mois = self.move_data.path_map[mpi] + .iter() + .filter(|moi| curr_move_out.contains(moi)) + .collect::>(); if mois.is_empty() { let item_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; - self.tcx.cannot_act_on_uninitialized_variable(span, - desired_action.as_noun(), - &self.describe_place(place) - .unwrap_or("_".to_owned()), - Origin::Mir) - .span_label(span, format!("use of possibly uninitialized {}", item_msg)) - .emit(); + self.tcx + .cannot_act_on_uninitialized_variable( + span, + desired_action.as_noun(), + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label(span, format!("use of possibly uninitialized {}", item_msg)) + .emit(); } else { let msg = ""; //FIXME: add "partially " or "collaterally " - let mut err = self.tcx.cannot_act_on_moved_value(span, - desired_action.as_noun(), - msg, - &self.describe_place(place) - .unwrap_or("_".to_owned()), - Origin::Mir); + let mut err = self.tcx.cannot_act_on_moved_value( + span, + desired_action.as_noun(), + msg, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); - err.span_label(span, format!("value {} here after move", - desired_action.as_verb_in_past_tense())); + err.span_label( + span, + format!( + "value {} here after move", + desired_action.as_verb_in_past_tense() + ), + ); for moi in mois { let move_msg = ""; //FIXME: add " (into closure)" let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span; if span == move_span { - err.span_label(span, - format!("value moved{} here in previous iteration of loop", - move_msg)); + err.span_label( + span, + format!("value moved{} here in previous iteration of loop", move_msg), + ); } else { err.span_label(move_span, format!("value moved{} here", move_msg)); }; @@ -1528,38 +1773,47 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn report_move_out_while_borrowed(&mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - borrow: &BorrowData<'tcx>) { + fn report_move_out_while_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { let value_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; let borrow_msg = match self.describe_place(&borrow.place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; - self.tcx.cannot_move_when_borrowed(span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir) - .span_label(self.retrieve_borrow_span(borrow), - format!("borrow of {} occurs here", borrow_msg)) - .span_label(span, format!("move out of {} occurs here", value_msg)) - .emit(); + self.tcx + .cannot_move_when_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label( + self.retrieve_borrow_span(borrow), + format!("borrow of {} occurs here", borrow_msg), + ) + .span_label(span, format!("move out of {} occurs here", value_msg)) + .emit(); } - fn report_use_while_mutably_borrowed(&mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - borrow : &BorrowData<'tcx>) { - + fn report_use_while_mutably_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { let mut err = self.tcx.cannot_use_when_mutably_borrowed( span, &self.describe_place(place).unwrap_or("_".to_owned()), self.retrieve_borrow_span(borrow), &self.describe_place(&borrow.place).unwrap_or("_".to_owned()), - Origin::Mir); + Origin::Mir, + ); err.emit(); } @@ -1602,22 +1856,25 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { return None; }; - self.tcx.with_freevars(node_id, |freevars| { - for (v, place) in freevars.iter().zip(places) { - match *place { - Operand::Copy(Place::Local(l)) | - Operand::Move(Place::Local(l)) if local == l => { - debug!( - "find_closure_span: found captured local {:?}", - l - ); - return Some(v.span); + self.tcx + .with_freevars(node_id, |freevars| { + for (v, place) in freevars.iter().zip(places) { + match *place { + Operand::Copy(Place::Local(l)) | + Operand::Move(Place::Local(l)) if local == l => + { + debug!( + "find_closure_span: found captured local {:?}", + l + ); + return Some(v.span); + } + _ => {} } - _ => {} } - } - None - }).map(|var_span| (args_span, var_span)) + None + }) + .map(|var_span| (args_span, var_span)) } else { None }; @@ -1628,13 +1885,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { None } - fn report_conflicting_borrow(&mut self, - context: Context, - common_prefix: &Place<'tcx>, - (place, span): (&Place<'tcx>, Span), - gen_borrow_kind: BorrowKind, - issued_borrow: &BorrowData, - end_issued_loan_span: Option) { + fn report_conflicting_borrow( + &mut self, + context: Context, + common_prefix: &Place<'tcx>, + (place, span): (&Place<'tcx>, Span), + gen_borrow_kind: BorrowKind, + issued_borrow: &BorrowData, + end_issued_loan_span: Option, + ) { use self::prefixes::IsPrefixOf; assert!(common_prefix.is_prefix_of(place)); @@ -1645,47 +1904,89 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let new_closure_span = self.find_closure_span(span, context.loc); let span = new_closure_span.map(|(args, _)| args).unwrap_or(span); let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location); - let issued_span = old_closure_span.map(|(args, _)| args).unwrap_or(issued_span); + let issued_span = old_closure_span + .map(|(args, _)| args) + .unwrap_or(issued_span); let desc_place = self.describe_place(place).unwrap_or("_".to_owned()); // FIXME: supply non-"" `opt_via` when appropriate - let mut err = match (gen_borrow_kind, "immutable", "mutable", - issued_borrow.kind, "immutable", "mutable") { + let mut err = match ( + gen_borrow_kind, + "immutable", + "mutable", + issued_borrow.kind, + "immutable", + "mutable", + ) { (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) | - (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => - self.tcx.cannot_reborrow_already_borrowed( - span, &desc_place, "", lft, issued_span, - "it", rgt, "", end_issued_loan_span, Origin::Mir), - - (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => - self.tcx.cannot_mutably_borrow_multiply( - span, &desc_place, "", issued_span, - "", end_issued_loan_span, Origin::Mir), - - (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => - self.tcx.cannot_uniquely_borrow_by_two_closures( - span, &desc_place, issued_span, - end_issued_loan_span, Origin::Mir), - - (BorrowKind::Unique, _, _, _, _, _) => - self.tcx.cannot_uniquely_borrow_by_one_closure( - span, &desc_place, "", - issued_span, "it", "", end_issued_loan_span, Origin::Mir), - - (_, _, _, BorrowKind::Unique, _, _) => - self.tcx.cannot_reborrow_already_uniquely_borrowed( - span, &desc_place, "it", "", - issued_span, "", end_issued_loan_span, Origin::Mir), - - (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => - unreachable!(), + (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx + .cannot_reborrow_already_borrowed( + span, + &desc_place, + "", + lft, + issued_span, + "it", + rgt, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx + .cannot_mutably_borrow_multiply( + span, + &desc_place, + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_uniquely_borrow_by_two_closures( + span, + &desc_place, + issued_span, + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure( + span, + &desc_place, + "", + issued_span, + "it", + "", + end_issued_loan_span, + Origin::Mir, + ), + + (_, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_reborrow_already_uniquely_borrowed( + span, + &desc_place, + "it", + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(), }; if let Some((_, var_span)) = old_closure_span { err.span_label( var_span, - format!("previous borrow occurs due to use of `{}` in closure", desc_place), + format!( + "previous borrow occurs due to use of `{}` in closure", + desc_place + ), ); } @@ -1699,16 +2000,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - fn report_borrowed_value_does_not_live_long_enough(&mut self, - _: Context, - (place, span): (&Place<'tcx>, Span), - end_span: Option) { + fn report_borrowed_value_does_not_live_long_enough( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + end_span: Option, + ) { let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); let proper_span = match *root_place { Place::Local(local) => self.mir.local_decls[local].source_info.span, - _ => span + _ => span, }; - let mut err = self.tcx.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); + let mut err = self.tcx + .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); err.span_label(proper_span, "temporary value created here"); err.span_label(span, "temporary value dropped here while still borrowed"); err.note("consider using a `let` binding to increase its lifetime"); @@ -1720,31 +2024,38 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - fn report_illegal_mutation_of_borrowed(&mut self, - _: Context, - (place, span): (&Place<'tcx>, Span), - loan: &BorrowData) { + fn report_illegal_mutation_of_borrowed( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + loan: &BorrowData, + ) { let mut err = self.tcx.cannot_assign_to_borrowed( span, self.retrieve_borrow_span(loan), &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir); + Origin::Mir, + ); err.emit(); } - fn report_illegal_reassignment(&mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - assigned_span: Span) { - let mut err = self.tcx.cannot_reassign_immutable(span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir); + fn report_illegal_reassignment( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + assigned_span: Span, + ) { + let mut err = self.tcx.cannot_reassign_immutable( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); err.span_label(span, "cannot assign twice to immutable variable"); if span != assigned_span { let value_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; err.span_label(assigned_span, format!("first assignment to {}", value_msg)); } @@ -1759,7 +2070,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let mut buf = String::new(); match self.append_place_to_string(place, &mut buf, false) { Ok(()) => Some(buf), - Err(()) => None + Err(()) => None, } } @@ -1769,33 +2080,35 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// of a closure type. fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option { match *place { - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Field(field, _ty) => { - let is_projection_from_ty_closure = proj.base.ty(self.mir, self.tcx) - .to_ty(self.tcx).is_closure(); - - if is_projection_from_ty_closure { - Some(field) - } else { - None - } - }, - _ => None + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Field(field, _ty) => { + let is_projection_from_ty_closure = proj.base + .ty(self.mir, self.tcx) + .to_ty(self.tcx) + .is_closure(); + + if is_projection_from_ty_closure { + Some(field) + } else { + None + } } + _ => None, }, - _ => None + _ => None, } } // Appends end-user visible description of `place` to `buf`. - fn append_place_to_string(&self, - place: &Place<'tcx>, - buf: &mut String, - mut autoderef: bool) -> Result<(), ()> { + fn append_place_to_string( + &self, + place: &Place<'tcx>, + buf: &mut String, + mut autoderef: bool, + ) -> Result<(), ()> { match *place { Place::Local(local) => { - self.append_local_to_string(local, buf,)?; + self.append_local_to_string(local, buf)?; } Place::Static(ref static_) => { buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id))); @@ -1819,10 +2132,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.append_place_to_string(&proj.base, buf, autoderef)?; } } - }, + } ProjectionElem::Downcast(..) => { self.append_place_to_string(&proj.base, buf, autoderef)?; - }, + } ProjectionElem::Field(field, _ty) => { autoderef = true; @@ -1835,7 +2148,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.append_place_to_string(&proj.base, buf, autoderef)?; buf.push_str(&format!(".{}", field_name)); } - }, + } ProjectionElem::Index(index) => { autoderef = true; @@ -1845,7 +2158,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { buf.push_str(".."); } buf.push_str("]"); - }, + } ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { autoderef = true; // Since it isn't possible to borrow an element on a particular index and @@ -1853,7 +2166,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // to avoid confusing the end-user self.append_place_to_string(&proj.base, buf, autoderef)?; buf.push_str(&"[..]"); - }, + } }; } } @@ -1869,8 +2182,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Some(name) => { buf.push_str(&format!("{}", name)); Ok(()) - }, - None => Err(()) + } + None => Err(()), } } @@ -1880,24 +2193,22 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Local(local) => { let local = &self.mir.local_decls[local]; self.describe_field_from_ty(&local.ty, field) - }, - Place::Static(ref static_) => { - self.describe_field_from_ty(&static_.ty, field) - }, - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => - self.describe_field(&proj.base, field), - ProjectionElem::Downcast(def, variant_index) => - format!("{}", def.variants[variant_index].fields[field.index()].name), - ProjectionElem::Field(_, field_type) => - self.describe_field_from_ty(&field_type, field), - ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => - format!("{}", self.describe_field(&proj.base, field)), - } } + Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Deref => self.describe_field(&proj.base, field), + ProjectionElem::Downcast(def, variant_index) => { + format!("{}", def.variants[variant_index].fields[field.index()].name) + } + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field) + } + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => { + format!("{}", self.describe_field(&proj.base, field)) + } + }, } } @@ -1906,26 +2217,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if ty.is_box() { // If the type is a box, the field is described from the boxed type self.describe_field_from_ty(&ty.boxed_ty(), field) - } - else { + } else { match ty.sty { - ty::TyAdt(def, _) => { - if def.is_enum() { - format!("{}", field.index()) - } - else { - format!("{}", def.struct_variant().fields[field.index()].name) - } - }, - ty::TyTuple(_, _) => { + ty::TyAdt(def, _) => if def.is_enum() { format!("{}", field.index()) + } else { + format!("{}", def.struct_variant().fields[field.index()].name) }, + ty::TyTuple(_, _) => format!("{}", field.index()), ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => { self.describe_field_from_ty(&tnm.ty, field) - }, - ty::TyArray(ty, _) | ty::TySlice(ty) => { - self.describe_field_from_ty(&ty, field) - }, + } + ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field), ty::TyClosure(closure_def_id, _) => { // Convert the def-id into a node-id. node-ids are only valid for // the local code in the current crate, so this returns an `Option` in case @@ -1935,11 +2238,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]); self.tcx.hir.name(freevar.var_id()).to_string() - } + } _ => { // Might need a revision when the fields in trait RFC is implemented // (https://github.com/rust-lang/rfcs/pull/1546) - bug!("End-user description not implemented for field access on `{:?}`", ty.sty); + bug!( + "End-user description not implemented for field access on `{:?}`", + ty.sty + ); } } } @@ -1969,8 +2275,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Local(..) | Place::Static(..) => return deepest, Place::Projection(ref proj) => proj, }; - if proj.elem == ProjectionElem::Deref && - place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() + if proj.elem == ProjectionElem::Deref + && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() { deepest = &proj.base; } @@ -2003,31 +2309,39 @@ enum ContextKind { } impl ContextKind { - fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } } + fn new(self, loc: Location) -> Context { + Context { + kind: self, + loc: loc, + } + } } impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { - pub(super) fn new(borrows: DataflowResults>, - inits: DataflowResults>, - uninits: DataflowResults>, - move_out: DataflowResults>, - ever_inits: DataflowResults>) - -> Self { + pub(super) fn new( + borrows: DataflowResults>, + inits: DataflowResults>, + uninits: DataflowResults>, + move_out: DataflowResults>, + ever_inits: DataflowResults>, + ) -> Self { InProgress { borrows: FlowInProgress::new(borrows), inits: FlowInProgress::new(inits), uninits: FlowInProgress::new(uninits), move_outs: FlowInProgress::new(move_out), - ever_inits: FlowInProgress::new(ever_inits) + ever_inits: FlowInProgress::new(ever_inits), } } - fn each_flow(&mut self, - mut xform_borrows: XB, - mut xform_inits: XI, - mut xform_uninits: XU, - mut xform_move_outs: XM, - mut xform_ever_inits: XE) where + fn each_flow( + &mut self, + mut xform_borrows: XB, + mut xform_inits: XI, + mut xform_uninits: XU, + mut xform_move_outs: XM, + mut xform_ever_inits: XE, + ) where XB: FnMut(&mut FlowInProgress>), XI: FnMut(&mut FlowInProgress>), XU: FnMut(&mut FlowInProgress>), @@ -2047,7 +2361,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("borrows in effect: ["); let mut saw_one = false; self.borrows.each_state_bit(|borrow| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; s.push_str(&format!("{}", borrow_data)); @@ -2057,7 +2373,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("borrows generated: ["); let mut saw_one = false; self.borrows.each_gen_bit(|borrow| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; s.push_str(&format!("{}", borrow_data)); @@ -2067,10 +2385,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("inits: ["); let mut saw_one = false; self.inits.each_state_bit(|mpi_init| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; - let move_path = - &self.inits.base_results.operator().move_data().move_paths[mpi_init]; + let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init]; s.push_str(&format!("{}", move_path)); }); s.push_str("] "); @@ -2078,7 +2397,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("uninits: ["); let mut saw_one = false; self.uninits.each_state_bit(|mpi_uninit| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let move_path = &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit]; @@ -2089,10 +2410,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("move_out: ["); let mut saw_one = false; self.move_outs.each_state_bit(|mpi_move_out| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; - let move_out = - &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; + let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; s.push_str(&format!("{:?}", move_out)); }); s.push_str("] "); @@ -2100,7 +2422,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("ever_init: ["); let mut saw_one = false; self.ever_inits.each_state_bit(|mpi_ever_init| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let ever_init = &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init]; @@ -2141,13 +2465,24 @@ impl<'b, 'gcx, 'tcx> FlowInProgress> { } } -impl FlowInProgress where BD: BitDenotation { - fn each_state_bit(&self, f: F) where F: FnMut(BD::Idx) { - self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f) +impl FlowInProgress +where + BD: BitDenotation, +{ + fn each_state_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.curr_state + .each_bit(self.base_results.operator().bits_per_block(), f) } - fn each_gen_bit(&self, f: F) where F: FnMut(BD::Idx) { - self.stmt_gen.each_bit(self.base_results.operator().bits_per_block(), f) + fn each_gen_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.stmt_gen + .each_bit(self.base_results.operator().bits_per_block(), f) } fn new(results: DataflowResults) -> Self { @@ -2172,9 +2507,13 @@ impl FlowInProgress where BD: BitDenotation { self.stmt_kill.reset_to_empty(); let mut ignored = IdxSetBuf::new_empty(0); let mut sets = BlockSets { - on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill, + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, }; - self.base_results.operator().statement_effect(&mut sets, loc); + self.base_results + .operator() + .statement_effect(&mut sets, loc); } fn reconstruct_terminator_effect(&mut self, loc: Location) { @@ -2182,9 +2521,13 @@ impl FlowInProgress where BD: BitDenotation { self.stmt_kill.reset_to_empty(); let mut ignored = IdxSetBuf::new_empty(0); let mut sets = BlockSets { - on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill, + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, }; - self.base_results.operator().terminator_effect(&mut sets, loc); + self.base_results + .operator() + .terminator_effect(&mut sets, loc); } fn apply_local_effect(&mut self) { @@ -2197,7 +2540,10 @@ impl FlowInProgress where BD: BitDenotation { self.curr_state.elems(univ) } - fn with_elems_outgoing(&self, f: F) where F: FnOnce(indexed_set::Elems) { + fn with_elems_outgoing(&self, f: F) + where + F: FnOnce(indexed_set::Elems), + { let mut curr_state = self.curr_state.clone(); curr_state.union(&self.stmt_gen); curr_state.subtract(&self.stmt_kill); From a9cb25b23aace3e8a7db3e64468dd84314a6d867 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Fri, 17 Nov 2017 04:34:02 -0500 Subject: [PATCH 04/30] inform constraint generation using maybe-init In particular, if we see a variable is DROP-LIVE, but it is not MAYBE-INIT, then we can ignore the drop. This leavess attempt to use more complex refinements of the idea (e.g., for subpaths or subfields) to future work. --- .../{borrow_check.rs => borrow_check/mod.rs} | 112 +++++++++++------- .../nll/constraint_generation.rs | 77 +++++++++--- .../nll/free_regions.rs | 0 .../{transform => borrow_check}/nll/mod.rs | 51 ++++++-- .../nll/region_infer.rs | 0 .../nll/renumber.rs | 0 .../nll/subtype_constraint_generation.rs | 0 src/librustc_mir/dataflow/impls/borrows.rs | 10 +- src/librustc_mir/dataflow/move_paths/mod.rs | 4 + src/librustc_mir/transform/mod.rs | 1 - ...initialized-drop-implicit-fragment-drop.rs | 33 ++++++ ...ialized-drop-implicit-fragment-drop.stderr | 11 ++ .../maybe-initialized-drop-uninitialized.rs | 28 +++++ ...aybe-initialized-drop-uninitialized.stderr | 0 .../maybe-initialized-drop-with-fragment.rs | 32 +++++ ...aybe-initialized-drop-with-fragment.stderr | 11 ++ ...lized-drop-with-uninitialized-fragments.rs | 34 ++++++ ...d-drop-with-uninitialized-fragments.stderr | 11 ++ src/test/ui/nll/maybe-initialized-drop.rs | 27 +++++ src/test/ui/nll/maybe-initialized-drop.stderr | 10 ++ 20 files changed, 374 insertions(+), 78 deletions(-) rename src/librustc_mir/{borrow_check.rs => borrow_check/mod.rs} (98%) rename src/librustc_mir/{transform => borrow_check}/nll/constraint_generation.rs (71%) rename src/librustc_mir/{transform => borrow_check}/nll/free_regions.rs (100%) rename src/librustc_mir/{transform => borrow_check}/nll/mod.rs (83%) rename src/librustc_mir/{transform => borrow_check}/nll/region_infer.rs (100%) rename src/librustc_mir/{transform => borrow_check}/nll/renumber.rs (100%) rename src/librustc_mir/{transform => borrow_check}/nll/subtype_constraint_generation.rs (100%) create mode 100644 src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs create mode 100644 src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr create mode 100644 src/test/ui/nll/maybe-initialized-drop-uninitialized.rs create mode 100644 src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr create mode 100644 src/test/ui/nll/maybe-initialized-drop-with-fragment.rs create mode 100644 src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr create mode 100644 src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs create mode 100644 src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr create mode 100644 src/test/ui/nll/maybe-initialized-drop.rs create mode 100644 src/test/ui/nll/maybe-initialized-drop.stderr diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check/mod.rs similarity index 98% rename from src/librustc_mir/borrow_check.rs rename to src/librustc_mir/borrow_check/mod.rs index ff38760cce699..446aba3d3d72c 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -18,7 +18,6 @@ use rustc::ty::maps::Providers; use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place}; use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; -use transform::nll; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_set::{self, IdxSetBuf}; @@ -39,6 +38,7 @@ use util::borrowck_errors::{BorrowckErrors, Origin}; use self::MutateMode::{JustWrite, WriteAndRead}; +pub(crate) mod nll; pub fn provide(providers: &mut Providers) { *providers = Providers { @@ -77,7 +77,21 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( .as_local_node_id(def_id) .expect("do_mir_borrowck: non-local DefId"); - let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx) { + // Make our own copy of the MIR. This copy will be modified (in place) to + // contain non-lexical lifetimes. It will have a lifetime tied + // to the inference context. + let mut mir: Mir<'tcx> = input_mir.clone(); + let free_regions = if !tcx.sess.opts.debugging_opts.nll { + None + } else { + let mir = &mut mir; + + // Replace all regions with fresh inference variables. + Some(nll::replace_regions_in_mir(infcx, def_id, mir)) + }; + let mir = &mir; + + let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { Ok(move_data) => move_data, Err((move_data, move_errors)) => { for move_error in move_errors { @@ -110,34 +124,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( } }; - // Make our own copy of the MIR. This copy will be modified (in place) to - // contain non-lexical lifetimes. It will have a lifetime tied - // to the inference context. - let mut mir: Mir<'tcx> = input_mir.clone(); - let mir = &mut mir; - - // If we are in non-lexical mode, compute the non-lexical lifetimes. - let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { - None - } else { - Some(nll::compute_regions(infcx, def_id, param_env, mir)) - }; - let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env, }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let flow_borrows = do_dataflow( - tcx, - mir, - id, - &attributes, - &dead_unwinds, - Borrows::new(tcx, mir, opt_regioncx.as_ref()), - |bd, i| bd.location(i), - ); - let flow_inits = do_dataflow( + let mut flow_inits = FlowInProgress::new(do_dataflow( tcx, mir, id, @@ -145,8 +137,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( &dead_unwinds, MaybeInitializedLvals::new(tcx, mir, &mdpe), |bd, i| &bd.move_data().move_paths[i], - ); - let flow_uninits = do_dataflow( + )); + let flow_uninits = FlowInProgress::new(do_dataflow( tcx, mir, id, @@ -154,8 +146,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( &dead_unwinds, MaybeUninitializedLvals::new(tcx, mir, &mdpe), |bd, i| &bd.move_data().move_paths[i], - ); - let flow_move_outs = do_dataflow( + )); + let flow_move_outs = FlowInProgress::new(do_dataflow( tcx, mir, id, @@ -163,8 +155,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( &dead_unwinds, MovingOutStatements::new(tcx, mir, &mdpe), |bd, i| &bd.move_data().moves[i], - ); - let flow_ever_inits = do_dataflow( + )); + let flow_ever_inits = FlowInProgress::new(do_dataflow( tcx, mir, id, @@ -172,7 +164,24 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( &dead_unwinds, EverInitializedLvals::new(tcx, mir, &mdpe), |bd, i| &bd.move_data().inits[i], - ); + )); + + // If we are in non-lexical mode, compute the non-lexical lifetimes. + let opt_regioncx = if let Some(free_regions) = free_regions { + Some(nll::compute_regions( + infcx, + def_id, + free_regions, + mir, + param_env, + &mut flow_inits, + &mdpe.move_data, + )) + } else { + assert!(!tcx.sess.opts.debugging_opts.nll); + None + }; + let flow_inits = flow_inits; // remove mut let mut mbcx = MirBorrowckCtxt { tcx: tcx, @@ -183,6 +192,16 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( storage_dead_or_drop_error_reported: FxHashSet(), }; + let flow_borrows = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + Borrows::new(tcx, mir, opt_regioncx), + |bd, i| bd.location(i), + )); + let mut state = InProgress::new( flow_borrows, flow_inits, @@ -2318,19 +2337,19 @@ impl ContextKind { } impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { - pub(super) fn new( - borrows: DataflowResults>, - inits: DataflowResults>, - uninits: DataflowResults>, - move_out: DataflowResults>, - ever_inits: DataflowResults>, + fn new( + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, + move_outs: FlowInProgress>, + ever_inits: FlowInProgress>, ) -> Self { InProgress { - borrows: FlowInProgress::new(borrows), - inits: FlowInProgress::new(inits), - uninits: FlowInProgress::new(uninits), - move_outs: FlowInProgress::new(move_out), - ever_inits: FlowInProgress::new(ever_inits), + borrows, + inits, + uninits, + move_outs, + ever_inits, } } @@ -2436,8 +2455,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { } } -impl<'b, 'gcx, 'tcx> FlowInProgress> { - fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { +impl<'tcx, T> FlowInProgress +where + T: HasMoveData<'tcx> + BitDenotation, +{ + fn has_any_child_of(&self, mpi: T::Idx) -> Option { let move_data = self.base_results.operator().move_data(); let mut todo = vec![mpi]; diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs similarity index 71% rename from src/librustc_mir/transform/nll/constraint_generation.rs rename to src/librustc_mir/borrow_check/nll/constraint_generation.rs index 73d5a610dbd53..460d49af20e51 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -9,7 +9,7 @@ // except according to those terms. use rustc::hir; -use rustc::mir::{Location, Place, Mir, Rvalue}; +use rustc::mir::{Local, Location, Place, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Place::Projection; use rustc::mir::{PlaceProjection, ProjectionElem}; @@ -20,17 +20,22 @@ use rustc::ty::fold::TypeFoldable; use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; +use borrow_check::FlowInProgress; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::{MoveData, HasMoveData}; use super::LivenessResults; use super::ToRegionVid; use super::region_infer::RegionInferenceContext; -pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, mir: &Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, liveness: &LivenessResults, + flow_inits: &mut FlowInProgress>, + move_data: &MoveData<'tcx>, ) { ConstraintGeneration { infcx, @@ -38,18 +43,23 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( mir, liveness, param_env, + flow_inits, + move_data, }.add_constraints(); } -struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, - regioncx: &'cx mut RegionInferenceContext<'tcx>, - mir: &'cx Mir<'tcx>, - liveness: &'cx LivenessResults, +/// 'cg = the duration of the constraint generation process itself. +struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>, + regioncx: &'cg mut RegionInferenceContext<'tcx>, + mir: &'cg Mir<'tcx>, + liveness: &'cg LivenessResults, param_env: ty::ParamEnv<'tcx>, + flow_inits: &'cg mut FlowInProgress>, + move_data: &'cg MoveData<'tcx>, } -impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { +impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { fn add_constraints(&mut self) { self.add_liveness_constraints(); self.add_borrow_constraints(); @@ -73,14 +83,51 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { } }); - self.liveness - .drop - .simulate_block(self.mir, bb, |location, live_locals| { - for live_local in live_locals.iter() { + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; + self.liveness.drop.simulate_block(self.mir, bb, |location, live_locals| { + all_live_locals.push((location, live_locals.iter().collect())); + }); + debug!("add_liveness_constraints: all_live_locals={:#?}", all_live_locals); + + let terminator_index = self.mir.basic_blocks()[bb].statements.len(); + self.flow_inits.reset_to_entry_of(bb); + while let Some((location, live_locals)) = all_live_locals.pop() { + for live_local in live_locals { + debug!("add_liveness_constraints: location={:?} live_local={:?}", location, + live_local); + + self.flow_inits.each_state_bit(|mpi_init| { + debug!("add_liveness_constraints: location={:?} initialized={:?}", + location, + &self.flow_inits + .base_results + .operator() + .move_data() + .move_paths[mpi_init]); + }); + + let mpi = self.move_data.rev_lookup.find_local(live_local); + if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { + debug!("add_liveness_constraints: mpi={:?} has initialized child {:?}", + self.move_data.move_paths[mpi], + self.move_data.move_paths[initialized_child]); + let live_local_ty = self.mir.local_decls[live_local].ty; self.add_drop_live_constraint(live_local_ty, location); } - }); + } + + if location.statement_index == terminator_index { + debug!("add_liveness_constraints: reconstruct_terminator_effect from {:#?}", + location); + self.flow_inits.reconstruct_terminator_effect(location); + } else { + debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}", + location); + self.flow_inits.reconstruct_statement_effect(location); + } + self.flow_inits.apply_local_effect(); + } } } @@ -219,7 +266,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { } } -impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { +impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/free_regions.rs similarity index 100% rename from src/librustc_mir/transform/nll/free_regions.rs rename to src/librustc_mir/borrow_check/nll/free_regions.rs diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs similarity index 83% rename from src/librustc_mir/transform/nll/mod.rs rename to src/librustc_mir/borrow_check/nll/mod.rs index 147f061ad113f..213cf52a8eb36 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -17,6 +17,9 @@ use std::collections::BTreeSet; use transform::MirSource; use transform::type_check; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; +use borrow_check::FlowInProgress; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::MoveData; use util as mir_util; use self::mir_util::PassWhere; @@ -24,27 +27,43 @@ use self::mir_util::PassWhere; mod constraint_generation; mod subtype_constraint_generation; mod free_regions; +use self::free_regions::FreeRegions; pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; mod renumber; -/// Computes the (non-lexical) regions from the input MIR. -/// -/// This may result in errors being reported. -pub fn compute_regions<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +/// Rewrites the regions in the MIR to use NLL variables, also +/// scraping out the set of free regions (e.g., region parameters) +/// declared on the function. That set will need to be given to +/// `compute_regions`. +pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, def_id: DefId, - param_env: ty::ParamEnv<'gcx>, mir: &mut Mir<'tcx>, -) -> RegionInferenceContext<'tcx> { +) -> FreeRegions<'tcx> { // Compute named region information. - let free_regions = &free_regions::free_regions(infcx, def_id); + let free_regions = free_regions::free_regions(infcx, def_id); // Replace all regions with fresh inference variables. - renumber::renumber_mir(infcx, free_regions, mir); + renumber::renumber_mir(infcx, &free_regions, mir); + + free_regions +} +/// Computes the (non-lexical) regions from the input MIR. +/// +/// This may result in errors being reported. +pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, + def_id: DefId, + free_regions: FreeRegions<'tcx>, + mir: &Mir<'tcx>, + param_env: ty::ParamEnv<'gcx>, + flow_inits: &mut FlowInProgress>, + move_data: &MoveData<'tcx>, +) -> RegionInferenceContext<'tcx> { // 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); @@ -52,8 +71,8 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( // 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); + 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 { @@ -75,7 +94,15 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( }; // Generate non-subtyping constraints. - constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness); + constraint_generation::generate_constraints( + infcx, + &mut regioncx, + &mir, + param_env, + liveness, + flow_inits, + move_data, + ); // Solve the region constraints. regioncx.solve(infcx, &mir); diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs similarity index 100% rename from src/librustc_mir/transform/nll/region_infer.rs rename to src/librustc_mir/borrow_check/nll/region_infer.rs diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs similarity index 100% rename from src/librustc_mir/transform/nll/renumber.rs rename to src/librustc_mir/borrow_check/nll/renumber.rs diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs similarity index 100% rename from src/librustc_mir/transform/nll/subtype_constraint_generation.rs rename to src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 19ab45dda9545..286ca768b16bf 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -21,8 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec}; use dataflow::{BitDenotation, BlockSets, DataflowOperator}; pub use dataflow::indexes::BorrowIndex; -use transform::nll::region_infer::RegionInferenceContext; -use transform::nll::ToRegionVid; +use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::ToRegionVid; use syntax_pos::Span; @@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, region_span_map: FxHashMap, - nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>, + nonlexical_regioncx: Option>, } // temporarily allow some dead fields: `kind` and `region` will be @@ -69,7 +69,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>) + nonlexical_regioncx: Option>) -> Self { let mut visitor = GatherBorrows { tcx, @@ -156,7 +156,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { fn kill_loans_out_of_scope_at_location(&self, sets: &mut BlockSets, location: Location) { - if let Some(regioncx) = self.nonlexical_regioncx { + if let Some(ref regioncx) = self.nonlexical_regioncx { for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { let borrow_region = borrow_data.region.to_region_vid(); if !regioncx.region_contains_point(borrow_region, location) { diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 294f48178a8ac..9d91e1344dc37 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -263,6 +263,10 @@ impl<'tcx> MovePathLookup<'tcx> { } } } + + pub fn find_local(&self, local: Local) -> MovePathIndex { + self.locals[local] + } } #[derive(Debug)] diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 830838c603758..fb9daf07c71dc 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -43,7 +43,6 @@ pub mod instcombine; pub mod copy_prop; pub mod generator; pub mod inline; -pub mod nll; pub mod lower_128bit; pub(crate) fn provide(providers: &mut Providers) { diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs new file mode 100644 index 0000000000000..0047f6d59237c --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs @@ -0,0 +1,33 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.b); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones +} diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr new file mode 100644 index 0000000000000..389334f9c1d8d --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr @@ -0,0 +1,11 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5 + | +27 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs new file mode 100644 index 0000000000000..64a4d39100063 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs @@ -0,0 +1,28 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + std::mem::drop(wrap); + x = 1; // OK, drop is inert +} diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs new file mode 100644 index 0000000000000..3242136f005e7 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -0,0 +1,32 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.a); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr new file mode 100644 index 0000000000000..9edeca2d18801 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr @@ -0,0 +1,11 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-with-fragment.rs:31:5 + | +27 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs new file mode 100644 index 0000000000000..3e32818b8dcf3 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.a); + std::mem::drop(foo.b); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + // FIXME ^ This currently errors and it should not. +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr new file mode 100644 index 0000000000000..24d0d6d04c8da --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr @@ -0,0 +1,11 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:32:5 + | +27 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs new file mode 100644 index 0000000000000..291fcbd73f3e1 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr new file mode 100644 index 0000000000000..7b1b55d133ac5 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -0,0 +1,10 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop.rs:26:5 + | +25 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +26 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + From 22b31758ecaf5384e82f258d6c7fad27feca411b Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Mon, 20 Nov 2017 16:23:26 -0800 Subject: [PATCH 05/30] region_infer: BitMatrix representation of region values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should be more efficient than allocating two BTreeSets for every region variable?—as it is written in #45670. --- src/librustc_mir/borrow_check/nll/mod.rs | 2 +- .../borrow_check/nll/region_infer.rs | 336 ++++++++++-------- 2 files changed, 184 insertions(+), 154 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 213cf52a8eb36..f0d425f3f1766 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -160,7 +160,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_str(region))?; }, // Before each basic block, dump out the values diff --git a/src/librustc_mir/borrow_check/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs index f60bd3c6ecec5..aac341380a00a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer.rs @@ -18,7 +18,9 @@ use rustc::mir::{Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; -use std::collections::BTreeSet; +use rustc_data_structures::bitvec::BitMatrix; +use rustc_data_structures::indexed_vec::Idx; +use std::collections::BTreeMap; use std::fmt; use syntax_pos::Span; @@ -33,15 +35,25 @@ pub struct RegionInferenceContext<'tcx> { /// regions, these start out empty and steadily grow, though for /// each free region R they start out containing the entire CFG /// and `end(R)`. - liveness_constraints: IndexVec, + /// + /// In this `BitMatrix` representation, the rows are the region + /// variables and the columns are the free regions and MIR locations. + liveness_constraints: BitMatrix, /// The final inferred values of the inference variables; `None` /// until `solve` is invoked. - inferred_values: Option>, + inferred_values: Option, /// The constraints we have accumulated and used during solving. constraints: Vec, + /// A map from each MIR Location to its column index in + /// `liveness_constraints`/`inferred_values`. (The first N columns are + /// the free regions.) + point_indices: BTreeMap, + + num_free_regions: usize, + free_region_map: &'tcx FreeRegionMap<'tcx>, } @@ -57,42 +69,6 @@ struct RegionDefinition<'tcx> { name: Option>, } -/// The value of an individual region variable. Region variables -/// consist of a set of points in the CFG as well as a set of "free -/// regions", which are sometimes written as `end(R)`. These -/// correspond to the named lifetimes and refer to portions of the -/// caller's control-flow graph -- specifically some portion that can -/// be reached after we return. -#[derive(Clone, Default, PartialEq, Eq)] -struct Region { - points: BTreeSet, - free_regions: BTreeSet, -} - -impl fmt::Debug for Region { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { - formatter - .debug_set() - .entries(&self.points) - .entries(&self.free_regions) - .finish() - } -} - -impl Region { - fn add_point(&mut self, point: Location) -> bool { - self.points.insert(point) - } - - fn add_free_region(&mut self, region: RegionVid) -> bool { - self.free_regions.insert(region) - } - - fn contains_point(&self, point: Location) -> bool { - self.points.contains(&point) - } -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Constraint { // NB. The ordering here is not significant for correctness, but @@ -119,6 +95,21 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// regions defined in `free_regions`. pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self { let num_region_variables = var_origins.len(); + let num_free_regions = free_regions.indices.len(); + + let mut num_points = 0; + let mut point_indices = BTreeMap::new(); + + for (block, block_data) in mir.basic_blocks().iter_enumerated() { + for statement_index in 0..block_data.statements.len() + 1 { + let location = Location { + block, + statement_index, + }; + point_indices.insert(location, num_free_regions + num_points); + num_points += 1; + } + } // Create a RegionDefinition for each inference variable. let definitions = var_origins @@ -127,14 +118,19 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect(); let mut result = Self { - definitions: definitions, - liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables), + definitions, + liveness_constraints: BitMatrix::new( + num_region_variables, + num_free_regions + num_points, + ), inferred_values: None, constraints: Vec::new(), + point_indices, + num_free_regions, free_region_map: free_regions.free_region_map, }; - result.init_free_regions(free_regions, mir); + result.init_free_regions(free_regions); result } @@ -158,7 +154,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// and (b) any free regions that it outlives, which in this case /// is just itself. R1 (`'b`) in contrast also outlives `'a` and /// hence contains R0 and R1. - fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) { + fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>) { let FreeRegions { indices, free_region_map: _, @@ -175,27 +171,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Initialize the name and a few other details. self.definitions[variable].name = Some(free_region); - // Add all nodes in the CFG to `definition.value`. - for (block, block_data) in mir.basic_blocks().iter_enumerated() { - let liveness_constraint = &mut self.liveness_constraints[variable]; - for statement_index in 0..block_data.statements.len() + 1 { - let location = Location { - block, - statement_index, - }; - liveness_constraint.add_point(location); - } + // Add all nodes in the CFG to liveness constraints + for (_location, point_index) in &self.point_indices { + self.liveness_constraints + .add(variable.index(), *point_index); } // Add `end(X)` into the set for X. - self.liveness_constraints[variable].add_free_region(variable); - - debug!( - "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`", - free_region, - variable, - self.liveness_constraints[variable], - ); + self.liveness_constraints + .add(variable.index(), variable.index()); } } @@ -206,27 +190,76 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns true if the region `r` contains the point `p`. /// - /// Until `solve()` executes, this value is not particularly meaningful. + /// Panics if called before `solve()` executes, pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { let inferred_values = self.inferred_values .as_ref() .expect("region values not yet inferred"); - inferred_values[r].contains_point(p) + self.region_contains_point_in_matrix(inferred_values, r, p) + } + + /// True if given region `r` contains the point `p`, when + /// evaluated in the set of region values `matrix`. + fn region_contains_point_in_matrix( + &self, + matrix: &BitMatrix, + r: RegionVid, + p: Location, + ) -> bool { + let point_index = self.point_indices + .get(&p) + .expect("point index should be known"); + matrix.contains(r.index(), *point_index) + } + + /// True if given region `r` contains the `end(s)`, when + /// evaluated in the set of region values `matrix`. + fn region_contains_region_in_matrix( + &self, + matrix: &BitMatrix, + r: RegionVid, + s: RegionVid + ) -> bool { + matrix.contains(r.index(), s.index()) } /// Returns access to the value of `r` for debugging purposes. - pub(super) fn region_value(&self, r: RegionVid) -> &fmt::Debug { + pub(super) fn region_value_str(&self, r: RegionVid) -> String { let inferred_values = self.inferred_values .as_ref() .expect("region values not yet inferred"); - &inferred_values[r] + + let mut result = String::new(); + result.push_str("{"); + let mut sep = ""; + + for &point in self.point_indices.keys() { + if self.region_contains_point_in_matrix(inferred_values, r, point) { + result.push_str(&format!("{}{:?}", sep, point)); + sep = ", "; + } + } + + for fr in (0 .. self.num_free_regions).map(RegionVid::new) { + if self.region_contains_region_in_matrix(inferred_values, r, fr) { + result.push_str(&format!("{}{:?}", sep, fr)); + sep = ", "; + } + } + + result.push_str("}"); + + result } /// Indicates that the region variable `v` is live at the point `point`. - pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) { + pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool { debug!("add_live_point({:?}, {:?})", v, point); assert!(self.inferred_values.is_none(), "values already inferred"); - self.liveness_constraints[v].add_point(point); + let point_index = self.point_indices + .get(&point) + .expect("point index should be known"); + self.liveness_constraints.add(v.index(), *point_index) } /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`. @@ -285,26 +318,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { let inferred_values = self.inferred_values.as_ref().unwrap(); let fr_name = fr_definition.name.unwrap(); - let fr_value = &inferred_values[fr]; + let fr_value = inferred_values.iter(fr.index()); // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). - for &outlived_fr in &fr_value.free_regions { + for outlived_fr in fr_value.take_while(|&i| i < self.num_free_regions) { // `fr` includes `end(fr)`, that's not especially // interesting. - if fr == outlived_fr { + if fr.index() == outlived_fr { continue; } - let outlived_fr_definition = &self.definitions[outlived_fr]; + let outlived_fr_definition = &self.definitions[RegionVid::new(outlived_fr)]; let outlived_fr_name = outlived_fr_definition.name.unwrap(); // Check that `o <= fr`. If not, report an error. if !self.free_region_map .sub_free_regions(outlived_fr_name, fr_name) { - // worst error msg ever - let blame_span = self.blame_span(fr, outlived_fr); + // FIXME: worst error msg ever + let blame_span = self.blame_span(fr, RegionVid::new(outlived_fr)); infcx.tcx.sess.span_err( blame_span, &format!( @@ -323,7 +356,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// feasible, but we check this later. fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { let mut changed = true; - let mut dfs = Dfs::new(mir); debug!("propagate_constraints()"); debug!("propagate_constraints: constraints={:#?}", { @@ -342,15 +374,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { for constraint in &self.constraints { debug!("propagate_constraints: constraint={:?}", constraint); - let sub = &inferred_values[constraint.sub].clone(); - let sup_value = &mut inferred_values[constraint.sup]; - // Grow the value as needed to accommodate the // outlives constraint. - if dfs.copy(sub, sup_value, constraint.point) { - debug!("propagate_constraints: sub={:?}", sub); - debug!("propagate_constraints: sup={:?}", sup_value); + if self.copy( + &mut inferred_values, + mir, + constraint.sub, + constraint.sup, + constraint.point, + ) { + debug!("propagate_constraints: sub={:?}", constraint.sub); + debug!("propagate_constraints: sup={:?}", constraint.sup); changed = true; } } @@ -360,72 +395,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } - /// Tries to finds a good span to blame for the fact that `fr1` - /// contains `fr2`. - fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { - // Find everything that influenced final value of `fr`. - let influenced_fr1 = self.dependencies(fr1); - - // Try to find some outlives constraint `'X: fr2` where `'X` - // influenced `fr1`. Blame that. - // - // NB, this is a pretty bad choice most of the time. In - // particular, the connection between `'X` and `fr1` may not - // be obvious to the user -- not to mention the naive notion - // of dependencies, which doesn't account for the locations of - // contraints at all. But it will do for now. - for constraint in &self.constraints { - if constraint.sub == fr2 && influenced_fr1[constraint.sup] { - return constraint.span; - } - } - - bug!( - "could not find any constraint to blame for {:?}: {:?}", - fr1, - fr2 - ); - } - - /// Finds all regions whose values `'a` may depend on in some way. - /// Basically if there exists a constraint `'a: 'b @ P`, then `'b` - /// and `dependencies('b)` will be in the final set. - /// - /// Used during error reporting, extremely naive and inefficient. - fn dependencies(&self, r0: RegionVid) -> IndexVec { - let mut result_set = IndexVec::from_elem(false, &self.definitions); - let mut changed = true; - result_set[r0] = true; - - while changed { - changed = false; - for constraint in &self.constraints { - if result_set[constraint.sup] { - if !result_set[constraint.sub] { - result_set[constraint.sub] = true; - changed = true; - } - } - } - } - - result_set - } -} - -struct Dfs<'a, 'tcx: 'a> { - mir: &'a Mir<'tcx>, -} - -impl<'a, 'tcx> Dfs<'a, 'tcx> { - fn new(mir: &'a Mir<'tcx>) -> Self { - Self { mir } - } - fn copy( - &mut self, - from_region: &Region, - to_region: &mut Region, + &self, + inferred_values: &mut BitMatrix, + mir: &Mir<'tcx>, + from_region: RegionVid, + to_region: RegionVid, start_point: Location, ) -> bool { let mut changed = false; @@ -435,9 +410,9 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { stack.push(start_point); while let Some(p) = stack.pop() { - debug!(" dfs: p={:?}", p); + debug!(" copy: p={:?}", p); - if !from_region.contains_point(p) { + if !self.region_contains_point_in_matrix(inferred_values, from_region, p) { debug!(" not in from-region"); continue; } @@ -447,9 +422,10 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { continue; } - changed |= to_region.add_point(p); + let point_index = self.point_indices.get(&p).unwrap(); + changed |= inferred_values.add(to_region.index(), *point_index); - let block_data = &self.mir[p.block]; + let block_data = &mir[p.block]; let successor_points = if p.statement_index < block_data.statements.len() { vec![ Location { @@ -475,10 +451,12 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { // If we reach the END point in the graph, then copy // over any skolemized end points in the `from_region` // and make sure they are included in the `to_region`. - - debug!(" dfs: free_regions={:?}", from_region.free_regions); - for &fr in &from_region.free_regions { - changed |= to_region.free_regions.insert(fr); + let free_region_indices = inferred_values + .iter(from_region.index()) + .take_while(|&i| i < self.num_free_regions) + .collect::>(); + for fr in &free_region_indices { + changed |= inferred_values.add(to_region.index(), *fr); } } else { stack.extend(successor_points); @@ -487,6 +465,58 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { changed } + + /// Tries to finds a good span to blame for the fact that `fr1` + /// contains `fr2`. + fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { + // Find everything that influenced final value of `fr`. + let influenced_fr1 = self.dependencies(fr1); + + // Try to find some outlives constraint `'X: fr2` where `'X` + // influenced `fr1`. Blame that. + // + // NB, this is a pretty bad choice most of the time. In + // particular, the connection between `'X` and `fr1` may not + // be obvious to the user -- not to mention the naive notion + // of dependencies, which doesn't account for the locations of + // contraints at all. But it will do for now. + for constraint in &self.constraints { + if constraint.sub == fr2 && influenced_fr1[constraint.sup] { + return constraint.span; + } + } + + bug!( + "could not find any constraint to blame for {:?}: {:?}", + fr1, + fr2 + ); + } + + /// Finds all regions whose values `'a` may depend on in some way. + /// Basically if there exists a constraint `'a: 'b @ P`, then `'b` + /// and `dependencies('b)` will be in the final set. + /// + /// Used during error reporting, extremely naive and inefficient. + fn dependencies(&self, r0: RegionVid) -> IndexVec { + let mut result_set = IndexVec::from_elem(false, &self.definitions); + let mut changed = true; + result_set[r0] = true; + + while changed { + changed = false; + for constraint in &self.constraints { + if result_set[constraint.sup] { + if !result_set[constraint.sub] { + result_set[constraint.sub] = true; + changed = true; + } + } + } + } + + result_set + } } impl<'tcx> RegionDefinition<'tcx> { From aa51603870de8d22122a20c0c3f3b0569753e8f9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Nov 2017 13:53:36 -0500 Subject: [PATCH 06/30] extract the `implied_bounds` code into a helper function --- src/librustc/infer/outlives/env.rs | 175 +--------------- src/librustc/infer/outlives/implied_bounds.rs | 195 ++++++++++++++++++ src/librustc/infer/outlives/mod.rs | 3 + 3 files changed, 201 insertions(+), 172 deletions(-) create mode 100644 src/librustc/infer/outlives/implied_bounds.rs diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 2099e923e0959..409230ba79b83 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -10,10 +10,8 @@ use middle::free_region::FreeRegionMap; use infer::{InferCtxt, GenericKind}; -use traits::FulfillmentContext; -use ty::{self, Ty, TypeFoldable}; -use ty::outlives::Component; -use ty::wf; +use infer::outlives::implied_bounds::ImpliedBound; +use ty::{self, Ty}; use syntax::ast; use syntax_pos::Span; @@ -44,24 +42,6 @@ pub struct OutlivesEnvironment<'tcx> { region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, } -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. -#[derive(Debug)] -enum ImpliedBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { let mut free_region_map = FreeRegionMap::new(); @@ -163,7 +143,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { for &ty in fn_sig_tys { let ty = infcx.resolve_type_vars_if_possible(&ty); debug!("add_implied_bounds: ty = {}", ty); - let implied_bounds = self.implied_bounds(infcx, body_id, ty, span); + let implied_bounds = infcx.implied_bounds(self.param_env, body_id, ty, span); // But also record other relationships, such as `T:'x`, // that don't go into the free-region-map but which we use @@ -203,153 +183,4 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { } } } - - /// Compute the implied bounds that a callee/impl can assume based on - /// the fact that caller/projector has ensured that `ty` is WF. See - /// the `ImpliedBound` type for more details. - fn implied_bounds( - &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - ty: Ty<'tcx>, - span: Span, - ) -> Vec> { - let tcx = infcx.tcx; - - // Sometimes when we ask what it takes for T: WF, we get back that - // U: WF is required; in that case, we push U onto this stack and - // process it next. Currently (at least) these resulting - // predicates are always guaranteed to be a subset of the original - // type, so we need not fear non-termination. - let mut wf_types = vec![ty]; - - let mut implied_bounds = vec![]; - - let mut fulfill_cx = FulfillmentContext::new(); - - while let Some(ty) = wf_types.pop() { - // Compute the obligations for `ty` to be well-formed. If `ty` is - // an unresolved inference variable, just substituted an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - let obligations = - wf::obligations(infcx, self.param_env, body_id, ty, span).unwrap_or(vec![]); - - // NB: All of these predicates *ought* to be easily proven - // true. In fact, their correctness is (mostly) implied by - // other parts of the program. However, in #42552, we had - // an annoying scenario where: - // - // - Some `T::Foo` gets normalized, resulting in a - // variable `_1` and a `T: Trait` constraint - // (not sure why it couldn't immediately get - // solved). This result of `_1` got cached. - // - These obligations were dropped on the floor here, - // rather than being registered. - // - Then later we would get a request to normalize - // `T::Foo` which would result in `_1` being used from - // the cache, but hence without the `T: Trait` - // constraint. As a result, `_1` never gets resolved, - // and we get an ICE (in dropck). - // - // Therefore, we register any predicates involving - // inference variables. We restrict ourselves to those - // involving inference variables both for efficiency and - // to avoids duplicate errors that otherwise show up. - fulfill_cx.register_predicate_obligations( - infcx, - obligations - .iter() - .filter(|o| o.predicate.has_infer_types()) - .cloned()); - - // From the full set of obligations, just filter down to the - // region relationships. - implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { - assert!(!obligation.has_escaping_regions()); - match obligation.predicate { - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::Projection(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ConstEvaluatable(..) => vec![], - - ty::Predicate::WellFormed(subty) => { - wf_types.push(subty); - vec![] - } - - ty::Predicate::RegionOutlives(ref data) => { - match tcx.no_late_bound_regions(data) { - None => vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => { - vec![ImpliedBound::RegionSubRegion(r_b, r_a)] - } - } - } - - ty::Predicate::TypeOutlives(ref data) => { - match tcx.no_late_bound_regions(data) { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = infcx.resolve_type_vars_if_possible(&ty_a); - let components = tcx.outlives_components(ty_a); - self.implied_bounds_from_components(r_b, components) - } - } - } - } - })); - } - - // Ensure that those obligations that we had to solve - // get solved *here*. - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => (), - Err(errors) => infcx.report_fulfillment_errors(&errors, None), - } - - implied_bounds - } - - /// When we have an implied bound that `T: 'a`, we can further break - /// this down to determine what relationships would have to hold for - /// `T: 'a` to hold. We get to assume that the caller has validated - /// those relationships. - fn implied_bounds_from_components( - &self, - sub_region: ty::Region<'tcx>, - sup_components: Vec>, - ) -> Vec> { - sup_components - .into_iter() - .flat_map(|component| { - match component { - Component::Region(r) => - vec![ImpliedBound::RegionSubRegion(sub_region, r)], - Component::Param(p) => - vec![ImpliedBound::RegionSubParam(sub_region, p)], - Component::Projection(p) => - vec![ImpliedBound::RegionSubProjection(sub_region, p)], - Component::EscapingProjection(_) => - // If the projection has escaping regions, don't - // try to infer any implied bounds even for its - // free components. This is conservative, because - // the caller will still have to prove that those - // free components outlive `sub_region`. But the - // idea is that the WAY that the caller proves - // that may change in the future and we want to - // give ourselves room to get smarter here. - vec![], - Component::UnresolvedInferenceVariable(..) => - vec![], - } - }) - .collect() - } } diff --git a/src/librustc/infer/outlives/implied_bounds.rs b/src/librustc/infer/outlives/implied_bounds.rs new file mode 100644 index 0000000000000..ae6961a071393 --- /dev/null +++ b/src/librustc/infer/outlives/implied_bounds.rs @@ -0,0 +1,195 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use infer::InferCtxt; +use syntax::ast; +use syntax::codemap::Span; +use traits::FulfillmentContext; +use ty::{self, Ty, TypeFoldable}; +use ty::outlives::Component; +use ty::wf; + +/// Implied bounds are region relationships that we deduce +/// automatically. The idea is that (e.g.) a caller must check that a +/// function's argument types are well-formed immediately before +/// calling that fn, and hence the *callee* can assume that its +/// argument types are well-formed. This may imply certain relationships +/// between generic parameters. For example: +/// +/// fn foo<'a,T>(x: &'a T) +/// +/// can only be called with a `'a` and `T` such that `&'a T` is WF. +/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +#[derive(Debug)] +pub enum ImpliedBound<'tcx> { + RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), + RegionSubParam(ty::Region<'tcx>, ty::ParamTy), + RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), +} + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Compute the implied bounds that a callee/impl can assume based on + /// the fact that caller/projector has ensured that `ty` is WF. See + /// the `ImpliedBound` type for more details. + /// + /// # Parameters + /// + /// - `param_env`, the where-clauses in scope + /// - `body_id`, the body-id to use when normalizing assoc types. + /// Note that this may cause outlives obligations to be injected + /// into the inference context with this body-id. + /// - `ty`, the type that we are supposed to assume is WF. + /// - `span`, a span to use when normalizing, hopefully not important, + /// might be useful if a `bug!` occurs. + pub fn implied_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ty: Ty<'tcx>, + span: Span, + ) -> Vec> { + let tcx = self.tcx; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Currently (at least) these resulting + // predicates are always guaranteed to be a subset of the original + // type, so we need not fear non-termination. + let mut wf_types = vec![ty]; + + let mut implied_bounds = vec![]; + + let mut fulfill_cx = FulfillmentContext::new(); + + while let Some(ty) = wf_types.pop() { + // Compute the obligations for `ty` to be well-formed. If `ty` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + let obligations = + wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]); + + // NB: All of these predicates *ought* to be easily proven + // true. In fact, their correctness is (mostly) implied by + // other parts of the program. However, in #42552, we had + // an annoying scenario where: + // + // - Some `T::Foo` gets normalized, resulting in a + // variable `_1` and a `T: Trait` constraint + // (not sure why it couldn't immediately get + // solved). This result of `_1` got cached. + // - These obligations were dropped on the floor here, + // rather than being registered. + // - Then later we would get a request to normalize + // `T::Foo` which would result in `_1` being used from + // the cache, but hence without the `T: Trait` + // constraint. As a result, `_1` never gets resolved, + // and we get an ICE (in dropck). + // + // Therefore, we register any predicates involving + // inference variables. We restrict ourselves to those + // involving inference variables both for efficiency and + // to avoids duplicate errors that otherwise show up. + fulfill_cx.register_predicate_obligations( + self, + obligations + .iter() + .filter(|o| o.predicate.has_infer_types()) + .cloned()); + + // From the full set of obligations, just filter down to the + // region relationships. + implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { + assert!(!obligation.has_escaping_regions()); + match obligation.predicate { + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::Projection(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ConstEvaluatable(..) => vec![], + + ty::Predicate::WellFormed(subty) => { + wf_types.push(subty); + vec![] + } + + ty::Predicate::RegionOutlives(ref data) => { + match tcx.no_late_bound_regions(data) { + None => vec![], + Some(ty::OutlivesPredicate(r_a, r_b)) => { + vec![ImpliedBound::RegionSubRegion(r_b, r_a)] + } + } + } + + ty::Predicate::TypeOutlives(ref data) => { + match tcx.no_late_bound_regions(data) { + None => vec![], + Some(ty::OutlivesPredicate(ty_a, r_b)) => { + let ty_a = self.resolve_type_vars_if_possible(&ty_a); + let components = tcx.outlives_components(ty_a); + Self::implied_bounds_from_components(r_b, components) + } + } + } + } + })); + } + + // Ensure that those obligations that we had to solve + // get solved *here*. + match fulfill_cx.select_all_or_error(self) { + Ok(()) => (), + Err(errors) => self.report_fulfillment_errors(&errors, None), + } + + implied_bounds + } + + /// When we have an implied bound that `T: 'a`, we can further break + /// this down to determine what relationships would have to hold for + /// `T: 'a` to hold. We get to assume that the caller has validated + /// those relationships. + fn implied_bounds_from_components( + sub_region: ty::Region<'tcx>, + sup_components: Vec>, + ) -> Vec> { + sup_components + .into_iter() + .flat_map(|component| { + match component { + Component::Region(r) => + vec![ImpliedBound::RegionSubRegion(sub_region, r)], + Component::Param(p) => + vec![ImpliedBound::RegionSubParam(sub_region, p)], + Component::Projection(p) => + vec![ImpliedBound::RegionSubProjection(sub_region, p)], + Component::EscapingProjection(_) => + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + vec![], + Component::UnresolvedInferenceVariable(..) => + vec![], + } + }) + .collect() + } +} diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index 0976c5f1f2fc4..30d6799266e1c 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -8,5 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Various code related to computing outlives relations. + pub mod env; +pub mod implied_bounds; mod obligations; From abd7d8813995337ff28c123db37011ceffcb3b26 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Nov 2017 14:59:17 -0500 Subject: [PATCH 07/30] move `free_regions_map` into `infer::outlives` --- src/librustc/infer/mod.rs | 5 +- src/librustc/infer/outlives/env.rs | 2 +- .../infer/outlives/free_region_map.rs | 138 ++++++++++++++++++ src/librustc/infer/outlives/mod.rs | 1 + src/librustc/middle/free_region.rs | 131 +---------------- src/librustc/traits/mod.rs | 2 +- src/librustc/ty/context.rs | 2 +- .../borrow_check/nll/free_regions.rs | 2 +- .../borrow_check/nll/region_infer.rs | 2 +- src/librustc_typeck/check/dropck.rs | 2 +- src/librustc_typeck/coherence/builtin.rs | 2 +- 11 files changed, 152 insertions(+), 137 deletions(-) create mode 100644 src/librustc/infer/outlives/free_region_map.rs diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 05b131dc355c2..72d8077b4c5ed 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -18,7 +18,7 @@ pub use ty::IntVarValue; pub use self::freshen::TypeFreshener; use hir::def_id::DefId; -use middle::free_region::{FreeRegionMap, RegionRelations}; +use middle::free_region::RegionRelations; use middle::region; use middle::lang_items; use mir::tcx::PlaceTy; @@ -44,6 +44,7 @@ use self::higher_ranked::HrMatchResult; use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins}; use self::lexical_region_resolve::LexicalRegionResolutions; +use self::outlives::free_region_map::FreeRegionMap; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -58,7 +59,7 @@ pub mod lattice; mod lub; pub mod region_constraints; mod lexical_region_resolve; -mod outlives; +pub mod outlives; pub mod resolve; mod freshen; mod sub; diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 409230ba79b83..67f19b2c50d05 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::free_region::FreeRegionMap; use infer::{InferCtxt, GenericKind}; +use infer::outlives::free_region_map::FreeRegionMap; use infer::outlives::implied_bounds::ImpliedBound; use ty::{self, Ty}; diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs new file mode 100644 index 0000000000000..1d235eb244d49 --- /dev/null +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -0,0 +1,138 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ty::{self, Lift, TyCtxt, Region}; +use rustc_data_structures::transitive_relation::TransitiveRelation; + +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct FreeRegionMap<'tcx> { + // Stores the relation `a < b`, where `a` and `b` are regions. + // + // Invariant: only free regions like `'x` or `'static` are stored + // in this relation, not scopes. + relation: TransitiveRelation> +} + +impl<'tcx> FreeRegionMap<'tcx> { + pub fn new() -> Self { + FreeRegionMap { relation: TransitiveRelation::new() } + } + + pub fn is_empty(&self) -> bool { + self.relation.is_empty() + } + + pub fn relate_free_regions_from_predicates(&mut self, + predicates: &[ty::Predicate<'tcx>]) { + debug!("relate_free_regions_from_predicates(predicates={:?})", predicates); + for predicate in predicates { + match *predicate { + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::WellFormed(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::TypeOutlives(..) | + ty::Predicate::ConstEvaluatable(..) => { + // No region bounds here + } + ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { + self.relate_regions(r_b, r_a); + } + } + } + } + + /// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. + /// (with the exception that `'static: 'x` is not notable) + pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { + debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); + if is_free_or_static(sub) && is_free(sup) { + self.relation.add(sub, sup) + } + } + + /// Tests whether `r_a <= sup`. Both must be free regions or + /// `'static`. + pub fn sub_free_regions<'a, 'gcx>(&self, + r_a: Region<'tcx>, + r_b: Region<'tcx>) + -> bool { + assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); + if let ty::ReStatic = r_b { + true // `'a <= 'static` is just always true, and not stored in the relation explicitly + } else { + r_a == r_b || self.relation.contains(&r_a, &r_b) + } + } + + /// Compute the least-upper-bound of two free regions. In some + /// cases, this is more conservative than necessary, in order to + /// avoid making arbitrary choices. See + /// `TransitiveRelation::postdom_upper_bound` for more details. + pub fn lub_free_regions<'a, 'gcx>(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>) + -> Region<'tcx> { + debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); + assert!(is_free(r_a)); + assert!(is_free(r_b)); + let result = if r_a == r_b { r_a } else { + match self.relation.postdom_upper_bound(&r_a, &r_b) { + None => tcx.mk_region(ty::ReStatic), + Some(r) => *r, + } + }; + debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); + result + } + + /// Returns all regions that are known to outlive `r_a`. For + /// example, in a function: + /// + /// ``` + /// fn foo<'a, 'b: 'a, 'c: 'b>() { .. } + /// ``` + /// + /// 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) || *r_a == ty::ReStatic); + self.relation.greater_than(&r_a) + } +} + +fn is_free(r: Region) -> bool { + match *r { + ty::ReEarlyBound(_) | ty::ReFree(_) => true, + _ => false + } +} + +fn is_free_or_static(r: Region) -> bool { + match *r { + ty::ReStatic => true, + _ => is_free(r), + } +} + +impl_stable_hash_for!(struct FreeRegionMap<'tcx> { + relation +}); + +impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { + type Lifted = FreeRegionMap<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option> { + self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx)) + .map(|relation| FreeRegionMap { relation }) + } +} diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index 30d6799266e1c..b20f274f0843a 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -11,5 +11,6 @@ //! Various code related to computing outlives relations. pub mod env; +pub mod free_region_map; pub mod implied_bounds; mod obligations; diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 3f0e6e2c28dd0..ca6a5dd7f5b0b 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -15,10 +15,10 @@ //! `TransitiveRelation` type and use that to decide when one free //! region outlives another and so forth. +use infer::outlives::free_region_map::FreeRegionMap; use hir::def_id::DefId; use middle::region; -use ty::{self, Lift, TyCtxt, Region}; -use rustc_data_structures::transitive_relation::TransitiveRelation; +use ty::{self, TyCtxt, Region}; /// Combines a `region::ScopeTree` (which governs relationships between /// scopes) and a `FreeRegionMap` (which governs relationships between @@ -103,7 +103,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> { ty::ReStatic => true, ty::ReEarlyBound(_) | ty::ReFree(_) => { let re_static = self.tcx.mk_region(ty::ReStatic); - self.free_regions.relation.contains(&re_static, &super_region) + self.free_regions.sub_free_regions(&re_static, &super_region) } _ => false } @@ -117,128 +117,3 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] -pub struct FreeRegionMap<'tcx> { - // Stores the relation `a < b`, where `a` and `b` are regions. - // - // Invariant: only free regions like `'x` or `'static` are stored - // in this relation, not scopes. - relation: TransitiveRelation> -} - -impl<'tcx> FreeRegionMap<'tcx> { - pub fn new() -> Self { - FreeRegionMap { relation: TransitiveRelation::new() } - } - - pub fn is_empty(&self) -> bool { - self.relation.is_empty() - } - - pub fn relate_free_regions_from_predicates(&mut self, - predicates: &[ty::Predicate<'tcx>]) { - debug!("relate_free_regions_from_predicates(predicates={:?})", predicates); - for predicate in predicates { - match *predicate { - ty::Predicate::Projection(..) | - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::WellFormed(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::TypeOutlives(..) | - ty::Predicate::ConstEvaluatable(..) => { - // No region bounds here - } - ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { - self.relate_regions(r_b, r_a); - } - } - } - } - - /// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - /// (with the exception that `'static: 'x` is not notable) - pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { - debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if is_free_or_static(sub) && is_free(sup) { - self.relation.add(sub, sup) - } - } - - /// Tests whether `r_a <= sup`. Both must be free regions or - /// `'static`. - pub fn sub_free_regions<'a, 'gcx>(&self, - r_a: Region<'tcx>, - r_b: Region<'tcx>) - -> bool { - assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); - if let ty::ReStatic = r_b { - true // `'a <= 'static` is just always true, and not stored in the relation explicitly - } else { - r_a == r_b || self.relation.contains(&r_a, &r_b) - } - } - - /// Compute the least-upper-bound of two free regions. In some - /// cases, this is more conservative than necessary, in order to - /// avoid making arbitrary choices. See - /// `TransitiveRelation::postdom_upper_bound` for more details. - pub fn lub_free_regions<'a, 'gcx>(&self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - r_a: Region<'tcx>, - r_b: Region<'tcx>) - -> Region<'tcx> { - debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); - assert!(is_free(r_a)); - assert!(is_free(r_b)); - let result = if r_a == r_b { r_a } else { - match self.relation.postdom_upper_bound(&r_a, &r_b) { - None => tcx.mk_region(ty::ReStatic), - Some(r) => *r, - } - }; - debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); - result - } - - /// Returns all regions that are known to outlive `r_a`. For - /// example, in a function: - /// - /// ``` - /// fn foo<'a, 'b: 'a, 'c: 'b>() { .. } - /// ``` - /// - /// 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) || *r_a == ty::ReStatic); - self.relation.greater_than(&r_a) - } -} - -fn is_free(r: Region) -> bool { - match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) => true, - _ => false - } -} - -fn is_free_or_static(r: Region) -> bool { - match *r { - ty::ReStatic => true, - _ => is_free(r), - } -} - -impl_stable_hash_for!(struct FreeRegionMap<'tcx> { - relation -}); - -impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { - type Lifted = FreeRegionMap<'tcx>; - fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option> { - self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx)) - .map(|relation| FreeRegionMap { relation }) - } -} diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 8d1bba4332276..ac08ff34518ee 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -17,9 +17,9 @@ pub use self::ObligationCauseCode::*; use hir; use hir::def_id::DefId; +use infer::outlives::free_region_map::FreeRegionMap; use middle::const_val::ConstEvalErr; use middle::region; -use middle::free_region::FreeRegionMap; use ty::subst::Substs; use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate}; use ty::error::{ExpectedFound, TypeError}; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 0ab769d4fe307..ce05acb01b001 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -23,10 +23,10 @@ use hir::map as hir_map; use hir::map::DefPathHash; use lint::{self, Lint}; use ich::{StableHashingContext, NodeIdHashingMode}; +use infer::outlives::free_region_map::FreeRegionMap; use middle::const_val::ConstVal; use middle::cstore::{CrateStore, LinkMeta}; use middle::cstore::EncodedMetadata; -use middle::free_region::FreeRegionMap; use middle::lang_items; use middle::resolve_lifetime::{self, ObjectLifetimeDefault}; use middle::stability; diff --git a/src/librustc_mir/borrow_check/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/free_regions.rs index 92a8a714d525a..7f984c72ee615 100644 --- a/src/librustc_mir/borrow_check/nll/free_regions.rs +++ b/src/librustc_mir/borrow_check/nll/free_regions.rs @@ -24,7 +24,7 @@ use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; -use rustc::middle::free_region::FreeRegionMap; +use rustc::infer::outlives::free_region_map::FreeRegionMap; use rustc::ty::{self, RegionVid}; use rustc::ty::subst::Substs; use rustc::util::nodemap::FxHashMap; diff --git a/src/librustc_mir/borrow_check/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs index aac341380a00a..b304ea4f7149a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer.rs @@ -13,7 +13,7 @@ use rustc::infer::InferCtxt; use rustc::infer::RegionVariableOrigin; use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::region_constraints::VarOrigins; -use rustc::middle::free_region::FreeRegionMap; +use rustc::infer::outlives::free_region_map::FreeRegionMap; use rustc::mir::{Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 610d07efa359d..f150c7db9b14d 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -11,8 +11,8 @@ use check::regionck::RegionCtxt; use hir::def_id::DefId; -use middle::free_region::FreeRegionMap; use rustc::infer::{self, InferOk}; +use rustc::infer::outlives::free_region_map::FreeRegionMap; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index fedfa51d61d11..01d0a7a3f857b 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -11,7 +11,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/translation. -use rustc::middle::free_region::FreeRegionMap; +use rustc::infer::outlives::free_region_map::FreeRegionMap; use rustc::middle::region; use rustc::middle::lang_items::UnsizeTraitLangItem; From 5562663a6cb010a3c155c873b2325c29033713f7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 1 Dec 2017 05:07:52 -0500 Subject: [PATCH 08/30] make `resolve_regions_and_report_errors` take an `OutlivesEnv` This revealed some shortcomings, one of which is fixed. Fixes #45937. --- src/librustc/infer/mod.rs | 8 ++-- src/librustc/infer/outlives/env.rs | 36 +++++++++++++++--- .../infer/outlives/free_region_map.rs | 27 +------------- src/librustc/traits/mod.rs | 10 +++-- src/librustc_driver/test.rs | 9 +++-- src/librustc_typeck/check/dropck.rs | 18 +++++++-- src/librustc_typeck/check/regionck.rs | 5 ++- src/librustc_typeck/coherence/builtin.rs | 11 ++++-- .../regions-normalize-in-where-clause-list.rs | 37 +++++++++++++++++++ 9 files changed, 108 insertions(+), 53 deletions(-) create mode 100644 src/test/compile-fail/regions-normalize-in-where-clause-list.rs diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 72d8077b4c5ed..96a980a15457e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -44,7 +44,7 @@ use self::higher_ranked::HrMatchResult; use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins}; use self::lexical_region_resolve::LexicalRegionResolutions; -use self::outlives::free_region_map::FreeRegionMap; +use self::outlives::env::OutlivesEnvironment; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -66,8 +66,6 @@ mod sub; pub mod type_variable; pub mod unify_key; -pub use self::outlives::env::OutlivesEnvironment; - #[must_use] pub struct InferOk<'tcx, T> { pub value: T, @@ -1158,7 +1156,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn resolve_regions_and_report_errors(&self, region_context: DefId, region_map: ®ion::ScopeTree, - free_regions: &FreeRegionMap<'tcx>) { + outlives_env: &OutlivesEnvironment<'tcx>) { assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), "region_obligations not empty: {:#?}", self.region_obligations.borrow()); @@ -1166,7 +1164,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let region_rels = &RegionRelations::new(self.tcx, region_context, region_map, - free_regions); + outlives_env.free_region_map()); let (var_origins, data) = self.region_constraints.borrow_mut() .take() .expect("regions already resolved") diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 67f19b2c50d05..9f00fc78cc0a4 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -44,14 +44,15 @@ pub struct OutlivesEnvironment<'tcx> { impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { - let mut free_region_map = FreeRegionMap::new(); - free_region_map.relate_free_regions_from_predicates(¶m_env.caller_bounds); - - OutlivesEnvironment { + let mut env = OutlivesEnvironment { param_env, - free_region_map, + free_region_map: FreeRegionMap::new(), region_bound_pairs: vec![], - } + }; + + env.init_free_regions_from_predicates(); + + env } /// Borrows current value of the `free_region_map`. @@ -183,4 +184,27 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { } } } + + fn init_free_regions_from_predicates(&mut self) { + debug!("init_free_regions_from_predicates()"); + for predicate in self.param_env.caller_bounds { + debug!("init_free_regions_from_predicates: predicate={:?}", predicate); + match *predicate { + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::WellFormed(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::TypeOutlives(..) | + ty::Predicate::ConstEvaluatable(..) => { + // No region bounds here + } + ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { + self.free_region_map.relate_regions(r_b, r_a); + } + } + } + } } diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs index 1d235eb244d49..36e0d6dba5e87 100644 --- a/src/librustc/infer/outlives/free_region_map.rs +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -29,31 +29,8 @@ impl<'tcx> FreeRegionMap<'tcx> { self.relation.is_empty() } - pub fn relate_free_regions_from_predicates(&mut self, - predicates: &[ty::Predicate<'tcx>]) { - debug!("relate_free_regions_from_predicates(predicates={:?})", predicates); - for predicate in predicates { - match *predicate { - ty::Predicate::Projection(..) | - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::WellFormed(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::TypeOutlives(..) | - ty::Predicate::ConstEvaluatable(..) => { - // No region bounds here - } - ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { - self.relate_regions(r_b, r_a); - } - } - } - } - - /// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - /// (with the exception that `'static: 'x` is not notable) + // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. + // (with the exception that `'static: 'x` is not notable) pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); if is_free_or_static(sub) && is_free(sup) { diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index ac08ff34518ee..d6f8a5f9cc6a1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -17,7 +17,7 @@ pub use self::ObligationCauseCode::*; use hir; use hir::def_id::DefId; -use infer::outlives::free_region_map::FreeRegionMap; +use infer::outlives::env::OutlivesEnvironment; use middle::const_val::ConstEvalErr; use middle::region; use ty::subst::Substs; @@ -554,9 +554,13 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, predicates); let region_scope_tree = region::ScopeTree::default(); - let free_regions = FreeRegionMap::new(); - infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); + + infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &outlives_env); + let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, Err(fixup_err) => { diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 78ce959e5c94e..0818b929ee7ad 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -17,7 +17,6 @@ use driver; use rustc_lint; use rustc_resolve::MakeGlobMap; use rustc_trans; -use rustc::middle::free_region::FreeRegionMap; use rustc::middle::region; use rustc::middle::resolve_lifetime; use rustc::ty::subst::{Kind, Subst}; @@ -25,6 +24,7 @@ use rustc::traits::{ObligationCause, Reveal}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::maps::OnDiskCache; use rustc::infer::{self, InferOk, InferResult}; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::infer::type_variable::TypeVariableOrigin; use rustc_metadata::cstore::CStore; use rustc::hir::map as hir_map; @@ -162,14 +162,15 @@ fn test_env(source_string: &str, |tcx| { tcx.infer_ctxt().enter(|infcx| { let mut region_scope_tree = region::ScopeTree::default(); + let param_env = ty::ParamEnv::empty(Reveal::UserFacing); body(Env { infcx: &infcx, region_scope_tree: &mut region_scope_tree, - param_env: ty::ParamEnv::empty(Reveal::UserFacing), + param_env: param_env, }); - let free_regions = FreeRegionMap::new(); + let outlives_env = OutlivesEnvironment::new(param_env); let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID); - infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &free_regions); + infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &outlives_env); assert_eq!(tcx.sess.err_count(), expected_err_count); }); }); diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index f150c7db9b14d..55700c452e57b 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -12,11 +12,11 @@ use check::regionck::RegionCtxt; use hir::def_id::DefId; use rustc::infer::{self, InferOk}; -use rustc::infer::outlives::free_region_map::FreeRegionMap; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::traits::{self, ObligationCause}; +use rustc::traits::{self, Reveal, ObligationCause}; use util::common::ErrorReported; use util::nodemap::FxHashSet; @@ -115,8 +115,18 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( } let region_scope_tree = region::ScopeTree::default(); - let free_regions = FreeRegionMap::new(); - infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &free_regions); + + // NB. It seems a bit... suspicious to use an empty param-env + // here. The correct thing, I imagine, would be + // `OutlivesEnvironment::new(impl_param_env)`, which would + // allow region solving to take any `a: 'b` relations on the + // impl into account. But I could not create a test case where + // it did the wrong thing, so I chose to preserve existing + // behavior, since it ought to be simply more + // conservative. -nmatsakis + let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty(Reveal::UserFacing)); + + infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env); Ok(()) }) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index b91137aeb68a5..7ef6027772be2 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -90,7 +90,8 @@ use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; -use rustc::infer::{self, OutlivesEnvironment}; +use rustc::infer; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::ty::adjustment; use rustc::ty::outlives::Component; @@ -553,7 +554,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_scope_tree, - self.outlives_environment.free_region_map()); + &self.outlives_environment); } fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index 01d0a7a3f857b..d63980eaa506b 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -11,7 +11,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/translation. -use rustc::infer::outlives::free_region_map::FreeRegionMap; +use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::middle::lang_items::UnsizeTraitLangItem; @@ -391,9 +391,12 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, resolve all regions. let region_scope_tree = region::ScopeTree::default(); - let mut free_regions = FreeRegionMap::new(); - free_regions.relate_free_regions_from_predicates(¶m_env.caller_bounds); - infcx.resolve_regions_and_report_errors(impl_did, ®ion_scope_tree, &free_regions); + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors( + impl_did, + ®ion_scope_tree, + &outlives_env, + ); CoerceUnsizedInfo { custom_kind: kind diff --git a/src/test/compile-fail/regions-normalize-in-where-clause-list.rs b/src/test/compile-fail/regions-normalize-in-where-clause-list.rs new file mode 100644 index 0000000000000..68642598ed2df --- /dev/null +++ b/src/test/compile-fail/regions-normalize-in-where-clause-list.rs @@ -0,0 +1,37 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we are able to normalize in the list of where-clauses, +// even if `'a: 'b` is required. + +trait Project<'a, 'b> { + type Item; +} + +impl<'a, 'b> Project<'a, 'b> for () + where 'a: 'b +{ + type Item = (); +} + +// No error here, we have 'a: 'b. We used to report an error here +// though, see https://github.com/rust-lang/rust/issues/45937. +fn foo<'a: 'b, 'b>() + where <() as Project<'a, 'b>>::Item : Eq +{ +} + +// Here we get an error: we need `'a: 'b`. +fn bar<'a, 'b>() //~ ERROR cannot infer + where <() as Project<'a, 'b>>::Item : Eq +{ +} + +fn main() { } From 1f33145ae91cbe095616c92ace63c93b61e5b4e9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 12 Nov 2017 05:04:26 -0500 Subject: [PATCH 09/30] make `no_late_bound_regions` a method on `Binder` --- src/librustc/infer/outlives/implied_bounds.rs | 4 ++-- src/librustc/infer/outlives/obligations.rs | 2 +- src/librustc/traits/fulfill.rs | 4 ++-- src/librustc/traits/project.rs | 2 +- src/librustc/traits/select.rs | 4 ++-- src/librustc/ty/fold.rs | 10 ---------- src/librustc/ty/sty.rs | 20 +++++++++++++++++++ src/librustc_mir/shim.rs | 2 +- src/librustc_mir/transform/lower_128bit.rs | 2 +- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/intrinsic.rs | 2 +- src/librustc_typeck/collect.rs | 2 +- 12 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/librustc/infer/outlives/implied_bounds.rs b/src/librustc/infer/outlives/implied_bounds.rs index ae6961a071393..452ceddd7d6e9 100644 --- a/src/librustc/infer/outlives/implied_bounds.rs +++ b/src/librustc/infer/outlives/implied_bounds.rs @@ -126,7 +126,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } ty::Predicate::RegionOutlives(ref data) => { - match tcx.no_late_bound_regions(data) { + match data.no_late_bound_regions() { None => vec![], Some(ty::OutlivesPredicate(r_a, r_b)) => { vec![ImpliedBound::RegionSubRegion(r_b, r_a)] @@ -135,7 +135,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } ty::Predicate::TypeOutlives(ref data) => { - match tcx.no_late_bound_regions(data) { + match data.no_late_bound_regions() { None => vec![], Some(ty::OutlivesPredicate(ty_a, r_b)) => { let ty_a = self.resolve_type_vars_if_possible(&ty_a); diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 3c3aba372fbd0..07eacde0aab88 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -604,7 +604,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { predicates .into_iter() .filter_map(|p| p.as_ref().to_opt_type_outlives()) - .filter_map(|p| self.tcx().no_late_bound_regions(&p)) + .filter_map(|p| p.no_late_bound_regions()) .filter(|p| p.0 == ty) .map(|p| p.1) .collect() diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index f56c3853de0ab..93e33836818ce 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -400,14 +400,14 @@ fn process_predicate<'a, 'gcx, 'tcx>( ty::Predicate::TypeOutlives(ref binder) => { // Check if there are higher-ranked regions. - match selcx.tcx().no_late_bound_regions(binder) { + match binder.no_late_bound_regions() { // If there are, inspect the underlying type further. None => { // Convert from `Binder>` to `Binder`. let binder = binder.map_bound_ref(|pred| pred.0); // Check if the type has any bound regions. - match selcx.tcx().no_late_bound_regions(&binder) { + match binder.no_late_bound_regions() { // If so, this obligation is an error (for now). Eventually we should be // able to support additional cases here, like `for<'a> &'a str: 'a`. None => { diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 0cc755dc42727..429771cca9844 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -1559,7 +1559,7 @@ impl<'cx, 'gcx, 'tcx> ProjectionCacheKey<'tcx> { let infcx = selcx.infcx(); // We don't do cross-snapshot caching of obligations with escaping regions, // so there's no cache key to use - infcx.tcx.no_late_bound_regions(&predicate) + predicate.no_late_bound_regions() .map(|predicate| ProjectionCacheKey { // We don't attempt to match up with a specific type-variable state // from a specific call to `opt_normalize_projection_type` - if diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 4bc3e2dd4d8d4..91e6c4270b32a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1834,7 +1834,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // T: Trait // so it seems ok if we (conservatively) fail to accept that `Unsize` // obligation above. Should be possible to extend this in the future. - let source = match self.tcx().no_late_bound_regions(&obligation.self_ty()) { + let source = match obligation.self_ty().no_late_bound_regions() { Some(t) => t, None => { // Don't add any candidates if there are bound regions. @@ -2784,7 +2784,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // assemble_candidates_for_unsizing should ensure there are no late bound // regions here. See the comment there for more details. let source = self.infcx.shallow_resolve( - tcx.no_late_bound_regions(&obligation.self_ty()).unwrap()); + obligation.self_ty().no_late_bound_regions().unwrap()); let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); let target = self.infcx.shallow_resolve(target); diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index bee119992230f..658596031832e 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -364,16 +364,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Binder(value) } - pub fn no_late_bound_regions(self, value: &Binder) -> Option - where T : TypeFoldable<'tcx> - { - if value.0.has_escaping_regions() { - None - } else { - Some(value.0.clone()) - } - } - /// Returns a set of all late-bound regions that are constrained /// by `value`, meaning that if we instantiate those LBR with /// variables and equate `value` with something else, those diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index caf78309cc238..c115b573a1c06 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -680,6 +680,26 @@ impl Binder { { ty::Binder(f(self.0)) } + + /// Unwraps and returns the value within, but only if it contains + /// no bound regions at all. (In other words, if this binder -- + /// and indeed any enclosing binder -- doesn't bind anything at + /// all.) Otherwise, returns `None`. + /// + /// (One could imagine having a method that just unwraps a single + /// binder, but permits late-bound regions bound by enclosing + /// binders, but that would require adjusting the debruijn + /// indices, and given the shallow binding structure we often use, + /// would not be that useful.) + pub fn no_late_bound_regions<'tcx>(self) -> Option + where T : TypeFoldable<'tcx> + { + if self.skip_binder().has_escaping_regions() { + None + } else { + Some(self.skip_binder().clone()) + } + } } /// Represents the projection of an associated type. In explicit UFCS diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 9ca044b764806..46193dedf8968 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -833,7 +833,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, let tcx = infcx.tcx; let gcx = tcx.global_tcx(); let def_id = tcx.hir.local_def_id(ctor_id); - let sig = gcx.no_late_bound_regions(&gcx.fn_sig(def_id)) + let sig = gcx.fn_sig(def_id).no_late_bound_regions() .expect("LBR in ADT constructor signature"); let sig = gcx.erase_regions(&sig); let param_env = gcx.param_env(def_id); diff --git a/src/librustc_mir/transform/lower_128bit.rs b/src/librustc_mir/transform/lower_128bit.rs index 7027d827c84f5..f8c45dd3d2570 100644 --- a/src/librustc_mir/transform/lower_128bit.rs +++ b/src/librustc_mir/transform/lower_128bit.rs @@ -144,7 +144,7 @@ fn check_lang_item_type<'a, 'tcx, D>( { let did = tcx.require_lang_item(lang_item); let poly_sig = tcx.fn_sig(did); - let sig = tcx.no_late_bound_regions(&poly_sig).unwrap(); + let sig = poly_sig.no_late_bound_regions().unwrap(); let lhs_ty = lhs.ty(local_decls, tcx); let rhs_ty = rhs.ty(local_decls, tcx); let place_ty = place.ty(local_decls, tcx).to_ty(tcx); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index f03f782ebb452..d5e4aa69c5b4e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -800,7 +800,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let pat_ty = self.instantiate_value_path(segments, opt_ty, def, pat.span, pat.id); // Replace constructor type with constructed type for tuple struct patterns. let pat_ty = pat_ty.fn_sig(tcx).output(); - let pat_ty = tcx.no_late_bound_regions(&pat_ty).expect("expected fn type"); + let pat_ty = pat_ty.no_late_bound_regions().expect("expected fn type"); self.demand_eqtype(pat.span, expected, pat_ty); diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index c1adb0e65a4da..23243c3ad66c0 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -389,7 +389,7 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut structural_to_nomimal = FxHashMap(); let sig = tcx.fn_sig(def_id); - let sig = tcx.no_late_bound_regions(&sig).unwrap(); + let sig = sig.no_late_bound_regions().unwrap(); if intr.inputs.len() != sig.inputs().len() { span_err!(tcx.sess, it.span, E0444, "platform-specific intrinsic has invalid number of \ diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7de29868d4341..b754c981b2101 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -202,7 +202,7 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { - if let Some(trait_ref) = self.tcx().no_late_bound_regions(&poly_trait_ref) { + if let Some(trait_ref) = poly_trait_ref.no_late_bound_regions() { self.tcx().mk_projection(item_def_id, trait_ref.substs) } else { // no late-bound regions, we can just ignore the binder From c45307fae1f261eac006816329780a7a01b2d0dc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 12 Nov 2017 05:25:13 -0500 Subject: [PATCH 10/30] extract the code to create `OutlivesBounds` into its own module Now it can be reused by the NLL code. --- src/librustc/infer/outlives/env.rs | 105 ++++++++---------- src/librustc/infer/outlives/implied_bounds.rs | 103 ++++++++++------- 2 files changed, 110 insertions(+), 98 deletions(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 9f00fc78cc0a4..43e782ac13018 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, GenericKind}; +use infer::{GenericKind, InferCtxt}; use infer::outlives::free_region_map::FreeRegionMap; -use infer::outlives::implied_bounds::ImpliedBound; +use infer::outlives::implied_bounds::{self, OutlivesBound}; use ty::{self, Ty}; use syntax::ast; @@ -50,7 +50,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { region_bound_pairs: vec![], }; - env.init_free_regions_from_predicates(); + env.add_outlives_bounds(None, implied_bounds::explicit_outlives_bounds(param_env)); env } @@ -144,65 +144,54 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { for &ty in fn_sig_tys { let ty = infcx.resolve_type_vars_if_possible(&ty); debug!("add_implied_bounds: ty = {}", ty); - let implied_bounds = infcx.implied_bounds(self.param_env, body_id, ty, span); - - // But also record other relationships, such as `T:'x`, - // that don't go into the free-region-map but which we use - // here. - for implication in implied_bounds { - debug!("add_implied_bounds: implication={:?}", implication); - match implication { - ImpliedBound::RegionSubRegion( - r_a @ &ty::ReEarlyBound(_), - &ty::ReVar(vid_b), - ) | - ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { - infcx.add_given(r_a, vid_b); - } - ImpliedBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs - .push((r_a, GenericKind::Param(param_b))); - } - ImpliedBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs - .push((r_a, GenericKind::Projection(projection_b))); - } - ImpliedBound::RegionSubRegion(r_a, r_b) => { - // In principle, we could record (and take - // advantage of) every relationship here, but - // we are also free not to -- it simply means - // strictly less that we can successfully type - // check. Right now we only look for things - // relationships between free regions. (It may - // also be that we should revise our inference - // system to be more general and to make use - // of *every* relationship that arises here, - // but presently we do not.) - self.free_region_map.relate_regions(r_a, r_b); - } - } - } + let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span); + self.add_outlives_bounds(Some(infcx), implied_bounds) } } - fn init_free_regions_from_predicates(&mut self) { - debug!("init_free_regions_from_predicates()"); - for predicate in self.param_env.caller_bounds { - debug!("init_free_regions_from_predicates: predicate={:?}", predicate); - match *predicate { - ty::Predicate::Projection(..) | - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::WellFormed(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::TypeOutlives(..) | - ty::Predicate::ConstEvaluatable(..) => { - // No region bounds here + /// Processes outlives bounds that are known to hold, whether from implied or other sources. + /// + /// The `infcx` parameter is optional; if the implied bounds may + /// contain inference variables, it should be supplied, in which + /// case we will register "givens" on the inference context. (See + /// `RegionConstraintData`.) + fn add_outlives_bounds( + &mut self, + infcx: Option<&InferCtxt<'a, 'gcx, 'tcx>>, + outlives_bounds: I, + ) where + I: IntoIterator>, + { + // But also record other relationships, such as `T:'x`, + // that don't go into the free-region-map but which we use + // here. + for outlives_bound in outlives_bounds { + debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); + match outlives_bound { + OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) | + OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { + infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); + } + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Param(param_b))); + } + OutlivesBound::RegionSubProjection(r_a, projection_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Projection(projection_b))); } - ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { - self.free_region_map.relate_regions(r_b, r_a); + OutlivesBound::RegionSubRegion(r_a, r_b) => { + // In principle, we could record (and take + // advantage of) every relationship here, but + // we are also free not to -- it simply means + // strictly less that we can successfully type + // check. Right now we only look for things + // relationships between free regions. (It may + // also be that we should revise our inference + // system to be more general and to make use + // of *every* relationship that arises here, + // but presently we do not.) + self.free_region_map.relate_regions(r_a, r_b); } } } diff --git a/src/librustc/infer/outlives/implied_bounds.rs b/src/librustc/infer/outlives/implied_bounds.rs index 452ceddd7d6e9..8a562471ac5d0 100644 --- a/src/librustc/infer/outlives/implied_bounds.rs +++ b/src/librustc/infer/outlives/implied_bounds.rs @@ -16,28 +16,32 @@ use ty::{self, Ty, TypeFoldable}; use ty::outlives::Component; use ty::wf; -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +/// Outlives bounds are relationships between generic parameters, +/// whether they both be regions (`'a: 'b`) or whether types are +/// involved (`T: 'a`). These relationships can be extracted from the +/// full set of predicates we understand or also from types (in which +/// case they are called implied bounds). They are fed to the +/// `OutlivesEnv` which in turn is supplied to the region checker and +/// other parts of the inference system. #[derive(Debug)] -pub enum ImpliedBound<'tcx> { +pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), } impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { - /// Compute the implied bounds that a callee/impl can assume based on - /// the fact that caller/projector has ensured that `ty` is WF. See - /// the `ImpliedBound` type for more details. + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// + /// fn foo<'a,T>(x: &'a T) + /// + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. /// /// # Parameters /// @@ -48,13 +52,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// - `ty`, the type that we are supposed to assume is WF. /// - `span`, a span to use when normalizing, hopefully not important, /// might be useful if a `bug!` occurs. - pub fn implied_bounds( + pub fn implied_outlives_bounds( &self, param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span, - ) -> Vec> { + ) -> Vec> { let tcx = self.tcx; // Sometimes when we ask what it takes for T: WF, we get back that @@ -76,8 +80,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { // than the ultimate set. (Note: normally there won't be // unresolved inference variables here anyway, but there might be // during typeck under some circumstances.) - let obligations = - wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]); + let obligations = wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]); // NB: All of these predicates *ought* to be easily proven // true. In fact, their correctness is (mostly) implied by @@ -105,7 +108,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { obligations .iter() .filter(|o| o.predicate.has_infer_types()) - .cloned()); + .cloned(), + ); // From the full set of obligations, just filter down to the // region relationships. @@ -125,25 +129,21 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { vec![] } - ty::Predicate::RegionOutlives(ref data) => { - match data.no_late_bound_regions() { - None => vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => { - vec![ImpliedBound::RegionSubRegion(r_b, r_a)] - } + ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() { + None => vec![], + Some(ty::OutlivesPredicate(r_a, r_b)) => { + vec![OutlivesBound::RegionSubRegion(r_b, r_a)] } - } + }, - ty::Predicate::TypeOutlives(ref data) => { - match data.no_late_bound_regions() { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = self.resolve_type_vars_if_possible(&ty_a); - let components = tcx.outlives_components(ty_a); - Self::implied_bounds_from_components(r_b, components) - } + ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() { + None => vec![], + Some(ty::OutlivesPredicate(ty_a, r_b)) => { + let ty_a = self.resolve_type_vars_if_possible(&ty_a); + let components = tcx.outlives_components(ty_a); + Self::implied_bounds_from_components(r_b, components) } - } + }, } })); } @@ -165,17 +165,17 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { fn implied_bounds_from_components( sub_region: ty::Region<'tcx>, sup_components: Vec>, - ) -> Vec> { + ) -> Vec> { sup_components .into_iter() .flat_map(|component| { match component { Component::Region(r) => - vec![ImpliedBound::RegionSubRegion(sub_region, r)], + vec![OutlivesBound::RegionSubRegion(sub_region, r)], Component::Param(p) => - vec![ImpliedBound::RegionSubParam(sub_region, p)], + vec![OutlivesBound::RegionSubParam(sub_region, p)], Component::Projection(p) => - vec![ImpliedBound::RegionSubProjection(sub_region, p)], + vec![OutlivesBound::RegionSubProjection(sub_region, p)], Component::EscapingProjection(_) => // If the projection has escaping regions, don't // try to infer any implied bounds even for its @@ -193,3 +193,26 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .collect() } } + +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator> + 'tcx { + debug!("explicit_outlives_bounds()"); + param_env + .caller_bounds + .into_iter() + .filter_map(move |predicate| match predicate { + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::WellFormed(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::TypeOutlives(..) | + ty::Predicate::ConstEvaluatable(..) => None, + ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map( + |ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a), + ), + }) +} From 31d61f1f86789869c675d446f03c4d21f8623b57 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 12 Nov 2017 05:26:26 -0500 Subject: [PATCH 11/30] rename `implied_bounds` module to `bounds` --- src/librustc/infer/outlives/{implied_bounds.rs => bounds.rs} | 0 src/librustc/infer/outlives/env.rs | 4 ++-- src/librustc/infer/outlives/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/librustc/infer/outlives/{implied_bounds.rs => bounds.rs} (100%) diff --git a/src/librustc/infer/outlives/implied_bounds.rs b/src/librustc/infer/outlives/bounds.rs similarity index 100% rename from src/librustc/infer/outlives/implied_bounds.rs rename to src/librustc/infer/outlives/bounds.rs diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 43e782ac13018..f7d94b47d7dfe 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -10,7 +10,7 @@ use infer::{GenericKind, InferCtxt}; use infer::outlives::free_region_map::FreeRegionMap; -use infer::outlives::implied_bounds::{self, OutlivesBound}; +use infer::outlives::bounds::{self, OutlivesBound}; use ty::{self, Ty}; use syntax::ast; @@ -50,7 +50,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { region_bound_pairs: vec![], }; - env.add_outlives_bounds(None, implied_bounds::explicit_outlives_bounds(param_env)); + env.add_outlives_bounds(None, bounds::explicit_outlives_bounds(param_env)); env } diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index b20f274f0843a..6aafebe79c671 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -12,5 +12,5 @@ pub mod env; pub mod free_region_map; -pub mod implied_bounds; +pub mod bounds; mod obligations; From 45f60271960c7a56a7d455366ec41a1e423d61a2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Nov 2017 13:28:01 -0500 Subject: [PATCH 12/30] break type-checking of aggregate-kind out into helper function --- src/librustc_mir/transform/type_check.rs | 97 +++++++++++++----------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 3b41b2545bf55..fc2a51e273819 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -1031,13 +1031,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn aggregate_field_ty( &mut self, - ak: &Box>, + ak: &AggregateKind<'tcx>, field_index: usize, location: Location, ) -> Result, FieldAccessError> { let tcx = self.tcx(); - match **ak { + match *ak { AggregateKind::Adt(def, variant_index, substs, active_field_index) => { let variant = &def.variants[variant_index]; let adj_field_index = active_field_index.unwrap_or(field_index); @@ -1069,56 +1069,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } } - AggregateKind::Array(ty) => { - Ok(ty) - } + AggregateKind::Array(ty) => Ok(ty), AggregateKind::Tuple => { unreachable!("This should have been covered in check_rvalues"); } } } - fn check_rvalue(&mut self, mir: &Mir<'tcx>, rv: &Rvalue<'tcx>, location: Location) { - let tcx = self.tcx(); - match rv { + fn check_rvalue(&mut self, mir: &Mir<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + match rvalue { Rvalue::Aggregate(ak, ops) => { - match **ak { - // tuple rvalue field type is always the type of the op. Nothing to check here. - AggregateKind::Tuple => {} - _ => { - for (i, op) in ops.iter().enumerate() { - let field_ty = match self.aggregate_field_ty(ak, i, location) { - Ok(field_ty) => field_ty, - Err(FieldAccessError::OutOfRange { field_count }) => { - span_mirbug!( - self, - rv, - "accessed field #{} but variant only has {}", - i, - field_count - ); - continue; - } - }; - let op_ty = op.ty(mir, tcx); - if let Err(terr) = self.sub_types( - op_ty, - field_ty, - location.at_successor_within_block(), - ) - { - span_mirbug!( - self, - rv, - "{:?} is not a subtype of {:?}: {:?}", - op_ty, - field_ty, - terr - ); - } - } - } - } + self.check_aggregate_rvalue(mir, rvalue, ak, ops, location) } // FIXME: These other cases have to be implemented in future PRs Rvalue::Use(..) | @@ -1134,6 +1095,52 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } + fn check_aggregate_rvalue( + &mut self, + mir: &Mir<'tcx>, + rvalue: &Rvalue<'tcx>, + aggregate_kind: &AggregateKind<'tcx>, + operands: &[Operand<'tcx>], + location: Location, + ) { + match aggregate_kind { + // tuple rvalue field type is always the type of the op. Nothing to check here. + AggregateKind::Tuple => return, + _ => {} + } + + let tcx = self.tcx(); + + for (i, operand) in operands.iter().enumerate() { + let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { + Ok(field_ty) => field_ty, + Err(FieldAccessError::OutOfRange { field_count }) => { + span_mirbug!( + self, + rvalue, + "accessed field #{} but variant only has {}", + i, + field_count + ); + continue; + } + }; + let operand_ty = operand.ty(mir, tcx); + if let Err(terr) = + self.sub_types(operand_ty, field_ty, location.at_successor_within_block()) + { + span_mirbug!( + self, + rvalue, + "{:?} is not a subtype of {:?}: {:?}", + operand_ty, + field_ty, + terr + ); + } + } + } + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); From fb4b06ab3b6ef6f621bd937c548168bddb9d442e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 20 Nov 2017 05:15:06 -0500 Subject: [PATCH 13/30] rename `greater_than` to `reachable_from` --- src/librustc/infer/outlives/free_region_map.rs | 13 ------------- src/librustc_data_structures/transitive_relation.rs | 5 +++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs index 36e0d6dba5e87..72db8622672f8 100644 --- a/src/librustc/infer/outlives/free_region_map.rs +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -73,19 +73,6 @@ impl<'tcx> FreeRegionMap<'tcx> { debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); result } - - /// Returns all regions that are known to outlive `r_a`. For - /// example, in a function: - /// - /// ``` - /// fn foo<'a, 'b: 'a, 'c: 'b>() { .. } - /// ``` - /// - /// 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) || *r_a == ty::ReStatic); - self.relation.greater_than(&r_a) - } } fn is_free(r: Region) -> bool { diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 933e08811ce5d..2419edf5ddcce 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -134,12 +134,13 @@ impl TransitiveRelation { } } - /// Returns a vector of all things greater than `a`. + /// Thinking of `x R y` as an edge `x -> y` in a graph, this + /// returns all things reachable from `a`. /// /// Really this probably ought to be `impl Iterator`, but /// I'm too lazy to make that work, and -- given the caching /// strategy -- it'd be a touch tricky anyhow. - pub fn greater_than(&self, a: &T) -> Vec<&T> { + pub fn reachable_from(&self, a: &T) -> Vec<&T> { match self.index(a) { Some(a) => self.with_closure(|closure| { closure.iter(a.0).map(|i| &self.elements[i]).collect() From 663e7a4f0849896c5a24f0a12b8e7d3b715d75dd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 20 Nov 2017 16:37:33 -0500 Subject: [PATCH 14/30] promote region_infer into its own module --- .../borrow_check/nll/{region_infer.rs => region_infer/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/librustc_mir/borrow_check/nll/{region_infer.rs => region_infer/mod.rs} (100%) diff --git a/src/librustc_mir/borrow_check/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs similarity index 100% rename from src/librustc_mir/borrow_check/nll/region_infer.rs rename to src/librustc_mir/borrow_check/nll/region_infer/mod.rs From 4de73368627067a0b35b09ea7319cef2b0358248 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 20 Nov 2017 16:41:48 -0500 Subject: [PATCH 15/30] extend TransitiveRelation with `parents` function --- .../transitive_relation.rs | 130 +++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 2419edf5ddcce..ffbb218dabedf 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -185,7 +185,14 @@ impl TransitiveRelation { /// b -> b1 /// ``` pub fn postdom_upper_bound(&self, a: &T, b: &T) -> Option<&T> { - let mut mubs = self.minimal_upper_bounds(a, b); + let mubs = self.minimal_upper_bounds(a, b); + self.mutual_immediate_postdominator(mubs) + } + + /// Viewing the relation as a graph, computes the "mutual + /// immediate postdominator" of a set of points (if one + /// exists). See `postdom_upper_bound` for details. + pub fn mutual_immediate_postdominator<'a>(&'a self, mut mubs: Vec<&'a T>) -> Option<&'a T> { loop { match mubs.len() { 0 => return None, @@ -277,6 +284,8 @@ impl TransitiveRelation { // After step 3, we know that no element can reach any of // its predecesssors (because of step 2) nor successors // (because we just called `pare_down`) + // + // This same algorithm is used in `parents` below. let mut candidates = closure.intersection(a.0, b.0); // (1) pare_down(&mut candidates, closure); // (2) @@ -291,6 +300,59 @@ impl TransitiveRelation { .collect() } + /// Given an element A, returns the maximal set {B} of elements B + /// such that + /// + /// - A != A + /// - A R B is true + /// - for each i, j: B[i] R B[j] does not hold + /// + /// The intuition is that this moves "one step up" through a lattice + /// (where the relation is encoding the `<=` relation for the lattice). + /// So e.g. if the relation is `->` and we have + /// + /// ``` + /// a -> b -> d -> f + /// | ^ + /// +--> c -> e ---+ + /// ``` + /// + /// then `parents(a)` returns `[b, c]`. The `postdom_parent` function + /// would further reduce this to just `f`. + pub fn parents(&self, a: &T) -> Vec<&T> { + let a = match self.index(a) { + Some(a) => a, + None => return vec![] + }; + + // Steal the algorithm for `minimal_upper_bounds` above, but + // with a slight tweak. In the case where `a R a`, we remove + // that from the set of candidates. + let ancestors = self.with_closure(|closure| { + let mut ancestors = closure.intersection(a.0, a.0); + + // Remove anything that can reach `a`. If this is a + // reflexive relation, this will include `a` itself. + ancestors.retain(|&e| !closure.contains(e, a.0)); + + pare_down(&mut ancestors, closure); // (2) + ancestors.reverse(); // (3a) + pare_down(&mut ancestors, closure); // (3b) + ancestors + }); + + ancestors.into_iter() + .rev() // (4) + .map(|i| &self.elements[i]) + .collect() + } + + /// A "best" parent in some sense. See `parents` and + /// `postdom_upper_bound` for more details. + pub fn postdom_parent(&self, a: &T) -> Option<&T> { + self.mutual_immediate_postdominator(self.parents(a)) + } + fn with_closure(&self, op: OP) -> R where OP: FnOnce(&BitMatrix) -> R { @@ -469,11 +531,17 @@ fn test_many_steps() { } #[test] -fn mubs_triange() { +fn mubs_triangle() { + // a -> tcx + // ^ + // | + // b let mut relation = TransitiveRelation::new(); relation.add("a", "tcx"); relation.add("b", "tcx"); assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"tcx"]); + assert_eq!(relation.parents(&"a"), vec![&"tcx"]); + assert_eq!(relation.parents(&"b"), vec![&"tcx"]); } #[test] @@ -499,6 +567,9 @@ fn mubs_best_choice1() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"2"]); + assert_eq!(relation.parents(&"0"), vec![&"2"]); + assert_eq!(relation.parents(&"2"), vec![&"1"]); + assert!(relation.parents(&"1").is_empty()); } #[test] @@ -523,6 +594,9 @@ fn mubs_best_choice2() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1"]); + assert_eq!(relation.parents(&"0"), vec![&"1"]); + assert_eq!(relation.parents(&"1"), vec![&"2"]); + assert!(relation.parents(&"2").is_empty()); } #[test] @@ -537,10 +611,15 @@ fn mubs_no_best_choice() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1", &"2"]); + assert_eq!(relation.parents(&"0"), vec![&"1", &"2"]); + assert_eq!(relation.parents(&"3"), vec![&"1", &"2"]); } #[test] fn mubs_best_choice_scc() { + // in this case, 1 and 2 form a cycle; we pick arbitrarily (but + // consistently). + let mut relation = TransitiveRelation::new(); relation.add("0", "1"); relation.add("0", "2"); @@ -552,6 +631,7 @@ fn mubs_best_choice_scc() { relation.add("3", "2"); assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1"]); + assert_eq!(relation.parents(&"0"), vec![&"1"]); } #[test] @@ -573,6 +653,8 @@ fn pdub_crisscross() { assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"a1", &"b1"]); assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"a"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"b"), Some(&"x")); } #[test] @@ -604,6 +686,9 @@ fn pdub_crisscross_more() { assert_eq!(relation.minimal_upper_bounds(&"a1", &"b1"), vec![&"a2", &"b2"]); assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x")); + + assert_eq!(relation.postdom_parent(&"a"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"b"), Some(&"x")); } #[test] @@ -621,6 +706,11 @@ fn pdub_lub() { assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"x"]); assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x")); + + assert_eq!(relation.postdom_parent(&"a"), Some(&"a1")); + assert_eq!(relation.postdom_parent(&"b"), Some(&"b1")); + assert_eq!(relation.postdom_parent(&"a1"), Some(&"x")); + assert_eq!(relation.postdom_parent(&"b1"), Some(&"x")); } #[test] @@ -722,3 +812,39 @@ fn mubs_scc_4() { assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"c"]); } + +#[test] +fn parent() { + // An example that was misbehaving in the compiler. + // + // 4 -> 1 -> 3 + // \ | / + // \ v / + // 2 -> 0 + // + // plus a bunch of self-loops + // + // Here `->` represents `<=` and `0` is `'static`. + + let pairs = vec![ + (2, /*->*/ 0), + (2, /*->*/ 2), + (0, /*->*/ 0), + (0, /*->*/ 0), + (1, /*->*/ 0), + (1, /*->*/ 1), + (3, /*->*/ 0), + (3, /*->*/ 3), + (4, /*->*/ 0), + (4, /*->*/ 1), + (1, /*->*/ 3), + ]; + + let mut relation = TransitiveRelation::new(); + for (a, b) in pairs { + relation.add(a, b); + } + + let p = relation.postdom_parent(&3); + assert_eq!(p, Some(&0)); +} From 49d2274cfe184f4a8ed490f7be5d523e02c33fa8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 20 Nov 2017 16:43:09 -0500 Subject: [PATCH 16/30] constraint_generation: create liveness constraints more thoroughly We now visit just the stuff in the CFG, and we add liveness constraints for all the random types, regions etc that appear within rvalues and statements. --- .../borrow_check/nll/constraint_generation.rs | 253 +++++++++++------- src/librustc_mir/dataflow/impls/borrows.rs | 4 - src/test/mir-opt/nll/region-liveness-basic.rs | 2 +- .../nll/region-liveness-two-disjoint-uses.rs | 4 +- .../mir-opt/nll/region-subtyping-basic.rs | 2 +- 5 files changed, 155 insertions(+), 110 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 460d49af20e51..42225536357da 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -9,20 +9,22 @@ // except according to those terms. use rustc::hir; -use rustc::mir::{Local, Location, Place, Mir, Rvalue}; +use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Place::Projection; -use rustc::mir::{PlaceProjection, ProjectionElem}; +use rustc::mir::{Local, PlaceProjection, ProjectionElem}; +use rustc::mir::visit::TyContext; use rustc::infer::InferCtxt; use rustc::traits::{self, ObligationCause}; -use rustc::ty::{self, Ty}; +use rustc::ty::{self, ClosureSubsts, Ty}; +use rustc::ty::subst::Substs; use rustc::ty::fold::TypeFoldable; use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; use borrow_check::FlowInProgress; use dataflow::MaybeInitializedLvals; -use dataflow::move_paths::{MoveData, HasMoveData}; +use dataflow::move_paths::{HasMoveData, MoveData}; use super::LivenessResults; use super::ToRegionVid; @@ -37,7 +39,7 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( flow_inits: &mut FlowInProgress>, move_data: &MoveData<'tcx>, ) { - ConstraintGeneration { + let mut cg = ConstraintGeneration { infcx, regioncx, mir, @@ -45,7 +47,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( param_env, flow_inits, move_data, - }.add_constraints(); + }; + + for (bb, data) in mir.basic_blocks().iter_enumerated() { + cg.visit_basic_block_data(bb, data); + } } /// 'cg = the duration of the constraint generation process itself. @@ -59,75 +65,147 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { move_data: &'cg MoveData<'tcx>, } -impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { - fn add_constraints(&mut self) { - self.add_liveness_constraints(); - self.add_borrow_constraints(); + +impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { + fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { + self.add_liveness_constraints(bb); + self.super_basic_block_data(bb, data); + } + + /// We sometimes have `substs` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_substs(&mut self, substs: &&'tcx Substs<'tcx>, location: Location) { + self.add_regular_live_constraint(*substs, location); + self.super_substs(substs); + } + + /// We sometimes have `region` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) { + self.add_regular_live_constraint(*region, location); + self.super_region(region); } + /// We sometimes have `ty` within an rvalue, or within a + /// 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::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); + } + } + + self.super_ty(ty); + } + + /// We sometimes have `closure_substs` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_closure_substs(&mut self, substs: &ClosureSubsts<'tcx>, location: Location) { + self.add_regular_live_constraint(*substs, location); + self.super_closure_substs(substs); + } + + 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); + } + + self.super_rvalue(rvalue, location); + } +} + +impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { /// Liveness constraints: /// /// > If a variable V is live at point P, then all regions R in the type of V /// > must include the point P. - fn add_liveness_constraints(&mut self) { - debug!("add_liveness_constraints()"); - for bb in self.mir.basic_blocks().indices() { - debug!("add_liveness_constraints: bb={:?}", bb); - - self.liveness - .regular - .simulate_block(self.mir, bb, |location, live_locals| { - for live_local in live_locals.iter() { - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_regular_live_constraint(live_local_ty, location); - } - }); + fn add_liveness_constraints(&mut self, bb: BasicBlock) { + debug!("add_liveness_constraints(bb={:?})", bb); - let mut all_live_locals: Vec<(Location, Vec)> = vec![]; - self.liveness.drop.simulate_block(self.mir, bb, |location, live_locals| { + self.liveness + .regular + .simulate_block(self.mir, bb, |location, live_locals| { + for live_local in live_locals.iter() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_regular_live_constraint(live_local_ty, location); + } + }); + + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; + self.liveness + .drop + .simulate_block(self.mir, bb, |location, live_locals| { all_live_locals.push((location, live_locals.iter().collect())); }); - debug!("add_liveness_constraints: all_live_locals={:#?}", all_live_locals); - - let terminator_index = self.mir.basic_blocks()[bb].statements.len(); - self.flow_inits.reset_to_entry_of(bb); - while let Some((location, live_locals)) = all_live_locals.pop() { - for live_local in live_locals { - debug!("add_liveness_constraints: location={:?} live_local={:?}", location, - live_local); - - self.flow_inits.each_state_bit(|mpi_init| { - debug!("add_liveness_constraints: location={:?} initialized={:?}", - location, - &self.flow_inits - .base_results - .operator() - .move_data() - .move_paths[mpi_init]); - }); - - let mpi = self.move_data.rev_lookup.find_local(live_local); - if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { - debug!("add_liveness_constraints: mpi={:?} has initialized child {:?}", - self.move_data.move_paths[mpi], - self.move_data.move_paths[initialized_child]); - - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_drop_live_constraint(live_local_ty, location); - } - } + debug!( + "add_liveness_constraints: all_live_locals={:#?}", + all_live_locals + ); - if location.statement_index == terminator_index { - debug!("add_liveness_constraints: reconstruct_terminator_effect from {:#?}", - location); - self.flow_inits.reconstruct_terminator_effect(location); - } else { - debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}", - location); - self.flow_inits.reconstruct_statement_effect(location); + let terminator_index = self.mir.basic_blocks()[bb].statements.len(); + self.flow_inits.reset_to_entry_of(bb); + while let Some((location, live_locals)) = all_live_locals.pop() { + for live_local in live_locals { + debug!( + "add_liveness_constraints: location={:?} live_local={:?}", + location, + live_local + ); + + self.flow_inits.each_state_bit(|mpi_init| { + debug!( + "add_liveness_constraints: location={:?} initialized={:?}", + location, + &self.flow_inits + .base_results + .operator() + .move_data() + .move_paths[mpi_init] + ); + }); + + let mpi = self.move_data.rev_lookup.find_local(live_local); + if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { + debug!( + "add_liveness_constraints: mpi={:?} has initialized child {:?}", + self.move_data.move_paths[mpi], + self.move_data.move_paths[initialized_child] + ); + + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); } - self.flow_inits.apply_local_effect(); } + + if location.statement_index == terminator_index { + debug!( + "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_terminator_effect(location); + } else { + debug!( + "add_liveness_constraints: reconstruct_statement_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_statement_effect(location); + } + self.flow_inits.apply_local_effect(); } } @@ -185,13 +263,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for outlive in outlives { - if let Some(ty) = outlive.as_type() { - self.add_regular_live_constraint(ty, location); - } else if let Some(r) = outlive.as_region() { - self.add_regular_live_constraint(r, location); - } else { - bug!() - } + self.add_regular_live_constraint(outlive, location); } // However, there may also be some types that @@ -228,10 +300,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { } } - fn add_borrow_constraints(&mut self) { - self.visit_mir(self.mir); - } - fn add_reborrow_constraint( &mut self, location: Location, @@ -246,43 +314,24 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { let base_ty = base.ty(self.mir, tcx).to_ty(tcx); let base_sty = &base_ty.sty; - if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty { + if let ty::TyRef(base_region, ty::TypeAndMut { ty: _, mutbl }) = *base_sty { match mutbl { - hir::Mutability::MutImmutable => { }, + hir::Mutability::MutImmutable => {} hir::Mutability::MutMutable => { self.add_reborrow_constraint(location, borrow_region, base); - }, + } } let span = self.mir.source_info(location).span; - self.regioncx.add_outlives(span, - base_region.to_region_vid(), - borrow_region.to_region_vid(), - location.successor_within_block()); + self.regioncx.add_outlives( + span, + base_region.to_region_vid(), + borrow_region.to_region_vid(), + location.successor_within_block(), + ); } } } } } - -impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { - 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_place) = *rvalue { - self.add_reborrow_constraint(location, region, borrowed_place); - } - - self.super_rvalue(rvalue, location); - } -} diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 286ca768b16bf..2bba3e263f3c6 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -132,10 +132,6 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { &self.borrows[idx].location } - pub fn nonlexical_regioncx(&self) -> Option<&'a RegionInferenceContext<'tcx>> { - self.nonlexical_regioncx - } - /// Returns the span for the "end point" given region. This will /// return `None` if NLL is enabled, since that concept has no /// meaning there. Otherwise, return region span if it exists and diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index 36dedeebd538a..cfbc51f9e1861 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,7 +31,7 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r: {bb2[1], bb3[0], bb3[1]} +// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]} // | '_#2r: {bb2[1], bb3[0], bb3[1]} // ... // let _2: &'_#2r usize; diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index de2b18fe4afa3..679f31fdab903 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -36,9 +36,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r: {bb2[1], bb3[0], bb3[1]} +// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]} // ... -// | '_#3r: {bb8[2], bb8[3], bb8[4]} +// | '_#3r: {bb8[1], bb8[2], bb8[3], bb8[4]} // | '_#4r: {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} // ... // let mut _2: &'_#4r usize; diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 6a2a7cc714997..471d77aefac62 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,7 +32,7 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#1r: {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} // | '_#2r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} // | '_#3r: {bb2[5], bb2[6], bb3[0], bb3[1]} // END rustc.main.nll.0.mir From e9067bd5cc5804edf75bb9d9e56956e0670c3ed1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 20 Nov 2017 16:45:12 -0500 Subject: [PATCH 17/30] MIR dump: restructure pretty to be more extensible by other code --- src/librustc_mir/borrow_check/nll/mod.rs | 3 +- src/librustc_mir/lib.rs | 3 + src/librustc_mir/util/mod.rs | 2 +- src/librustc_mir/util/pretty.rs | 371 +++++++++++++++-------- 4 files changed, 246 insertions(+), 133 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index f0d425f3f1766..a04cc113e723d 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -170,7 +170,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( writeln!(out, " | Live variables on entry to {:?}: {}", bb, s)?; } - PassWhere::InCFG(location) => { + PassWhere::BeforeLocation(location) => { let s = live_variable_set( ®ular_liveness_per_location[&location], &drop_liveness_per_location[&location], @@ -178,6 +178,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( writeln!(out, " | Live variables at {:?}: {}", location, s)?; } + PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} } Ok(()) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index af309342dc522..53f9b885ac6c6 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -18,12 +18,15 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(catch_expr)] #![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] #![feature(i128_type)] +#![feature(inclusive_range_syntax)] #![feature(match_default_bindings)] +#![feature(range_contains)] #![feature(rustc_diagnostic_macros)] #![feature(placement_in_syntax)] #![feature(collection_placement)] diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index feea0e2880974..eebe5a86018ea 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -15,7 +15,7 @@ pub mod patch; mod alignment; mod graphviz; -mod pretty; +pub(crate) mod pretty; pub mod liveness; pub use self::alignment::is_disaligned; diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 4a0090204851c..8a3db0eb25b99 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -14,17 +14,17 @@ use rustc::mir::*; use rustc::ty::TyCtxt; use rustc::ty::item_path; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::{Idx}; +use rustc_data_structures::indexed_vec::Idx; use std::fmt::Display; use std::fs; use std::io::{self, Write}; -use std::path::{PathBuf, Path}; +use std::path::{Path, PathBuf}; use super::graphviz::write_mir_fn_graphviz; use transform::MirSource; const INDENT: &'static str = " "; /// Alignment for lining up comments following MIR statements -const ALIGN: usize = 40; +pub(crate) const ALIGN: usize = 40; /// An indication of where we are in the control flow graph. Used for printing /// extra information in `dump_mir` @@ -38,8 +38,11 @@ pub enum PassWhere { /// We are about to start dumping the given basic block. BeforeBlock(BasicBlock), - /// We are just about to dumpt the given statement or terminator. - InCFG(Location), + /// We are just about to dump the given statement or terminator. + BeforeLocation(Location), + + /// We just dumped the given statement or terminator. + AfterLocation(Location), } /// If the session is properly configured, dumps a human-readable @@ -56,64 +59,124 @@ pub enum PassWhere { /// - `substring1&substring2,...` -- `&`-separated list of substrings /// that can appear in the pass-name or the `item_path_str` for the given /// node-id. If any one of the substrings match, the data is dumped out. -pub fn dump_mir<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - pass_num: Option<&Display>, - pass_name: &str, - disambiguator: &Display, - source: MirSource, - mir: &Mir<'tcx>, - extra_data: F) -where - F: FnMut(PassWhere, &mut Write) -> io::Result<()> +pub fn dump_mir<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_num: Option<&Display>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, + mir: &Mir<'tcx>, + extra_data: F, +) where + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, { if !dump_enabled(tcx, pass_name, source) { return; } - let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below + let node_path = item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 below tcx.item_path_str(source.def_id) }); - dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, - disambiguator, source, mir, extra_data); + dump_matched_mir_node( + tcx, + pass_num, + pass_name, + &node_path, + disambiguator, + source, + mir, + extra_data, + ); } -pub fn dump_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - pass_name: &str, - source: MirSource) - -> bool { +pub fn dump_enabled<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_name: &str, + source: MirSource, +) -> bool { let filters = match tcx.sess.opts.debugging_opts.dump_mir { None => return false, Some(ref filters) => filters, }; - let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below + let node_path = item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 below tcx.item_path_str(source.def_id) }); - filters.split("&") - .any(|filter| { - filter == "all" || - pass_name.contains(filter) || - node_path.contains(filter) - }) + filters.split("&").any(|filter| { + filter == "all" || pass_name.contains(filter) || node_path.contains(filter) + }) } // #41697 -- we use `with_forced_impl_filename_line()` because // `item_path_str()` would otherwise trigger `type_of`, and this can // run while we are already attempting to evaluate `type_of`. -fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - pass_num: Option<&Display>, - pass_name: &str, - node_path: &str, - disambiguator: &Display, - source: MirSource, - mir: &Mir<'tcx>, - mut extra_data: F) -where - F: FnMut(PassWhere, &mut Write) -> io::Result<()> +fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + pass_num: Option<&Display>, + pass_name: &str, + node_path: &str, + disambiguator: &Display, + source: MirSource, + mir: &Mir<'tcx>, + mut extra_data: F, +) where + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, { + let _: io::Result<()> = do catch { + let mut file = create_dump_file( + tcx, + "mir", + pass_num, + pass_name, + disambiguator, + source, + )?; + writeln!(file, "// MIR for `{}`", node_path)?; + writeln!(file, "// source = {:?}", source)?; + writeln!(file, "// pass_name = {}", pass_name)?; + writeln!(file, "// disambiguator = {}", disambiguator)?; + if let Some(ref layout) = mir.generator_layout { + writeln!(file, "// generator_layout = {:?}", layout)?; + } + writeln!(file, "")?; + extra_data(PassWhere::BeforeCFG, &mut file)?; + write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?; + extra_data(PassWhere::AfterCFG, &mut file)?; + Ok(()) + }; + + if tcx.sess.opts.debugging_opts.dump_mir_graphviz { + let _: io::Result<()> = do catch { + let mut file = create_dump_file( + tcx, + "dot", + pass_num, + pass_name, + disambiguator, + source, + )?; + write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; + Ok(()) + }; + } +} + +/// Returns the path to the filename where we should dump a given MIR. +/// Also used by other bits of code (e.g., NLL inference) that dump +/// graphviz data or other things. +fn dump_path( + tcx: TyCtxt<'_, '_, '_>, + extension: &str, + pass_num: Option<&Display>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, +) -> PathBuf { let promotion_id = match source.promoted { Some(id) => format!("-{:?}", id), - None => String::new() + None => String::new(), }; let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { @@ -126,48 +189,64 @@ where }; let mut file_path = PathBuf::new(); + if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir { let p = Path::new(file_dir); file_path.push(p); }; - let _ = fs::create_dir_all(&file_path); - let item_name = tcx.hir.def_path(source.def_id).to_filename_friendly_no_crate(); - let file_name = format!("rustc.{}{}{}.{}.{}.mir", - item_name, promotion_id, pass_num, pass_name, disambiguator); + let item_name = tcx.hir + .def_path(source.def_id) + .to_filename_friendly_no_crate(); + + let file_name = format!( + "rustc.{}{}{}.{}.{}.{}", + item_name, + promotion_id, + pass_num, + pass_name, + disambiguator, + extension, + ); + file_path.push(&file_name); - let _ = fs::File::create(&file_path).and_then(|mut file| { - writeln!(file, "// MIR for `{}`", node_path)?; - writeln!(file, "// source = {:?}", source)?; - writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file, "// disambiguator = {}", disambiguator)?; - if let Some(ref layout) = mir.generator_layout { - writeln!(file, "// generator_layout = {:?}", layout)?; - } - writeln!(file, "")?; - extra_data(PassWhere::BeforeCFG, &mut file)?; - write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?; - extra_data(PassWhere::AfterCFG, &mut file)?; - Ok(()) - }); - if tcx.sess.opts.debugging_opts.dump_mir_graphviz { - file_path.set_extension("dot"); - let _ = fs::File::create(&file_path).and_then(|mut file| { - write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; - Ok(()) - }); + file_path +} + +/// Attempts to open a file where we should dump a given MIR or other +/// bit of MIR-related data. Used by `mir-dump`, but also by other +/// bits of code (e.g., NLL inference) that dump graphviz data or +/// other things, and hence takes the extension as an argument. +pub(crate) fn create_dump_file( + tcx: TyCtxt<'_, '_, '_>, + extension: &str, + pass_num: Option<&Display>, + pass_name: &str, + disambiguator: &Display, + source: MirSource, +) -> io::Result { + let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); + if let Some(parent) = file_path.parent() { + fs::create_dir_all(parent)?; } + fs::File::create(&file_path) } /// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - single: Option, - w: &mut Write) - -> io::Result<()> -{ - writeln!(w, "// WARNING: This output format is intended for human consumers only")?; - writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; +pub fn write_mir_pretty<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + single: Option, + w: &mut Write, +) -> io::Result<()> { + writeln!( + w, + "// WARNING: This output format is intended for human consumers only" + )?; + writeln!( + w, + "// and is subject to change without notice. Knock yourself out." + )?; let mut first = true; for def_id in dump_mir_def_ids(tcx, single) { @@ -186,7 +265,7 @@ pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, writeln!(w, "")?; let src = MirSource { def_id, - promoted: Some(i) + promoted: Some(i), }; write_mir_fn(tcx, src, mir, &mut |_, _| Ok(()), w)?; } @@ -194,14 +273,15 @@ pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, Ok(()) } -pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - src: MirSource, - mir: &Mir<'tcx>, - extra_data: &mut F, - w: &mut Write) - -> io::Result<()> +pub fn write_mir_fn<'a, 'gcx, 'tcx, F>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + src: MirSource, + mir: &Mir<'tcx>, + extra_data: &mut F, + w: &mut Write, +) -> io::Result<()> where - F: FnMut(PassWhere, &mut Write) -> io::Result<()> + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, { write_mir_intro(tcx, src, mir, w)?; for block in mir.basic_blocks().indices() { @@ -217,14 +297,15 @@ where } /// Write out a human-readable textual representation for the given basic block. -pub fn write_basic_block(tcx: TyCtxt, - block: BasicBlock, - mir: &Mir, - extra_data: &mut F, - w: &mut Write) - -> io::Result<()> +pub fn write_basic_block( + tcx: TyCtxt, + block: BasicBlock, + mir: &Mir, + extra_data: &mut F, + w: &mut Write, +) -> io::Result<()> where - F: FnMut(PassWhere, &mut Write) -> io::Result<()> + F: FnMut(PassWhere, &mut Write) -> io::Result<()>, { let data = &mir[block]; @@ -234,43 +315,61 @@ where writeln!(w, "{0:1$}{2}", lbl, ALIGN, cleanup_text)?; // List of statements in the middle. - let mut current_location = Location { block: block, statement_index: 0 }; + let mut current_location = Location { + block: block, + statement_index: 0, + }; for statement in &data.statements { - extra_data(PassWhere::InCFG(current_location), w)?; + extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_mir = format!("{0}{0}{1:?};", INDENT, statement); - writeln!(w, "{0:1$} // {2}", - indented_mir, - ALIGN, - comment(tcx, statement.source_info))?; + writeln!( + w, + "{:A$} // {:?}: {}", + indented_mir, + current_location, + comment(tcx, statement.source_info), + A = ALIGN, + )?; + extra_data(PassWhere::AfterLocation(current_location), w)?; current_location.statement_index += 1; } // Terminator at the bottom. - extra_data(PassWhere::InCFG(current_location), w)?; + extra_data(PassWhere::BeforeLocation(current_location), w)?; let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); - writeln!(w, "{0:1$} // {2}", - indented_terminator, - ALIGN, - comment(tcx, data.terminator().source_info))?; + writeln!( + w, + "{:A$} // {:?}: {}", + indented_terminator, + current_location, + comment(tcx, data.terminator().source_info), + A = ALIGN, + )?; + extra_data(PassWhere::AfterLocation(current_location), w)?; writeln!(w, "{}}}", INDENT) } fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String { - format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span)) + format!( + "scope {} at {}", + scope.index(), + tcx.sess.codemap().span_to_string(span) + ) } /// Prints user-defined variables in a scope tree. /// /// Returns the total number of variables printed. -fn write_scope_tree(tcx: TyCtxt, - mir: &Mir, - scope_tree: &FxHashMap>, - w: &mut Write, - parent: VisibilityScope, - depth: usize) - -> io::Result<()> { +fn write_scope_tree( + tcx: TyCtxt, + mir: &Mir, + scope_tree: &FxHashMap>, + w: &mut Write, + parent: VisibilityScope, + depth: usize, +) -> io::Result<()> { let indent = depth * INDENT.len(); let children = match scope_tree.get(&parent) { @@ -300,17 +399,22 @@ fn write_scope_tree(tcx: TyCtxt, }; let indent = indent + INDENT.len(); - let indented_var = format!("{0:1$}let {2}{3:?}: {4};", - INDENT, - indent, - mut_str, - local, - var.ty); - writeln!(w, "{0:1$} // \"{2}\" in {3}", - indented_var, - ALIGN, - name, - comment(tcx, source_info))?; + let indented_var = format!( + "{0:1$}let {2}{3:?}: {4:?};", + INDENT, + indent, + mut_str, + local, + var.ty + ); + writeln!( + w, + "{0:1$} // \"{2}\" in {3}", + indented_var, + ALIGN, + name, + comment(tcx, source_info) + )?; } write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?; @@ -323,11 +427,12 @@ fn write_scope_tree(tcx: TyCtxt, /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). -pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - src: MirSource, - mir: &Mir, - w: &mut Write) - -> io::Result<()> { +pub fn write_mir_intro<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + src: MirSource, + mir: &Mir, + w: &mut Write, +) -> io::Result<()> { write_mir_sig(tcx, src, mir, w)?; writeln!(w, " {{")?; @@ -335,9 +440,10 @@ pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, let mut scope_tree: FxHashMap> = FxHashMap(); for (index, scope_data) in mir.visibility_scopes.iter().enumerate() { if let Some(parent) = scope_data.parent_scope { - scope_tree.entry(parent) - .or_insert(vec![]) - .push(VisibilityScope::new(index)); + scope_tree + .entry(parent) + .or_insert(vec![]) + .push(VisibilityScope::new(index)); } else { // Only the argument scope has no parent, because it's the root. assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index()); @@ -363,9 +469,7 @@ pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, Ok(()) } -fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) - -> io::Result<()> -{ +fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) -> io::Result<()> { let id = tcx.hir.as_local_node_id(src.def_id).unwrap(); let body_owner_kind = tcx.hir.body_owner_kind(id); match (body_owner_kind, src.promoted) { @@ -376,7 +480,8 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) (hir::BodyOwnerKind::Static(hir::MutMutable), _) => write!(w, "static mut")?, } - item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere + item_path::with_forced_impl_filename_line(|| { + // see notes on #41697 elsewhere write!(w, " {}", tcx.item_path_str(src.def_id)) })?; @@ -394,9 +499,7 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) write!(w, ") -> {}", mir.return_ty()) } - (hir::BodyOwnerKind::Const, _) | - (hir::BodyOwnerKind::Static(_), _) | - (_, Some(_)) => { + (hir::BodyOwnerKind::Const, _) | (hir::BodyOwnerKind::Static(_), _) | (_, Some(_)) => { assert_eq!(mir.arg_count, 0); write!(w, ": {} =", mir.return_ty()) } @@ -406,7 +509,13 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> { // Compiler-introduced temporary types. for temp in mir.temps_iter() { - writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?; + writeln!( + w, + "{}let mut {:?}: {};", + INDENT, + temp, + mir.local_decls[temp].ty + )?; } Ok(()) From 7f247add4144df0e1fd3951a1d5aa6451c8a8eb2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Nov 2017 11:17:48 -0500 Subject: [PATCH 18/30] move `liberate_late_bound_regions` to a method on the tcx No reason for it to live on `Inherited`. --- src/librustc/ty/fold.rs | 25 +++++++++++++++++++++ src/librustc_typeck/check/closure.rs | 2 +- src/librustc_typeck/check/compare_method.rs | 2 +- src/librustc_typeck/check/mod.rs | 18 +-------------- src/librustc_typeck/check/wfcheck.rs | 8 +++---- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 658596031832e..fbbc0e92bcdba 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -40,6 +40,7 @@ //! and does not need to visit anything else. use middle::const_val::ConstVal; +use hir::def_id::DefId; use ty::{self, Binder, Ty, TyCtxt, TypeFlags}; use std::fmt; @@ -329,6 +330,14 @@ struct RegionReplacer<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { + /// Replace all regions bound by the given `Binder` with the + /// results returned by the closure; the closure is expected to + /// return a free region (relative to this binder), and hence the + /// binder is removed in the return type. The closure is invoked + /// once for each unique `BoundRegion`; multiple references to the + /// same `BoundRegion` will reuse the previous result. A map is + /// returned at the end with each bound region and the free region + /// that replaced it. pub fn replace_late_bound_regions(self, value: &Binder, mut f: F) @@ -341,6 +350,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { (result, replacer.map) } + /// Replace any late-bound regions bound in `value` with + /// free variants attached to `all_outlive_scope`. + pub fn liberate_late_bound_regions( + &self, + all_outlive_scope: DefId, + value: &ty::Binder + ) -> T + where T: TypeFoldable<'tcx> { + self.replace_late_bound_regions(value, |br| { + self.mk_region(ty::ReFree(ty::FreeRegion { + scope: all_outlive_scope, + bound_region: br + })) + }).0 + } + /// Flattens two binding levels into one. So `for<'a> for<'b> Foo` /// becomes `for<'a,'b> Foo`. pub fn flatten_late_bound_regions(self, bound2_value: &Binder>) diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 5b5d697bcf435..147347a75abe8 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -562,7 +562,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { body: &hir::Body, bound_sig: ty::PolyFnSig<'tcx>, ) -> ClosureSignatures<'tcx> { - let liberated_sig = self.liberate_late_bound_regions(expr_def_id, &bound_sig); + let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, &bound_sig); let liberated_sig = self.inh.normalize_associated_types_in( body.value.span, body.value.id, diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 24efb79170427..70607bf4842a5 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -270,7 +270,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let impl_fty = tcx.mk_fn_ptr(ty::Binder(impl_sig)); debug!("compare_impl_method: impl_fty={:?}", impl_fty); - let trait_sig = inh.liberate_late_bound_regions( + let trait_sig = tcx.liberate_late_bound_regions( impl_m.def_id, &tcx.fn_sig(trait_m.def_id)); let trait_sig = diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 09dd334a62d8a..27a0e4f6dfe63 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -698,22 +698,6 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); self.register_infer_ok_obligations(ok) } - - /// Replace any late-bound regions bound in `value` with - /// free variants attached to `all_outlive_scope`. - fn liberate_late_bound_regions(&self, - all_outlive_scope: DefId, - value: &ty::Binder) - -> T - where T: TypeFoldable<'tcx> - { - self.tcx.replace_late_bound_regions(value, |br| { - self.tcx.mk_region(ty::ReFree(ty::FreeRegion { - scope: all_outlive_scope, - bound_region: br - })) - }).0 - } } struct CheckItemTypesVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } @@ -882,7 +866,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Compute the fty from point of view of inside fn. let fn_sig = - inh.liberate_late_bound_regions(def_id, &fn_sig); + tcx.liberate_late_bound_regions(def_id, &fn_sig); let fn_sig = inh.normalize_associated_types_in(body.value.span, body_id.node_id, diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 0ca259fd44d78..d4625bb58c338 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -451,7 +451,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { implied_bounds: &mut Vec>) { let sig = fcx.normalize_associated_types_in(span, &sig); - let sig = fcx.liberate_late_bound_regions(def_id, &sig); + let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig); for input_ty in sig.inputs() { fcx.register_wf_obligation(&input_ty, span, self.code.clone()); @@ -484,12 +484,12 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { let sig = fcx.tcx.fn_sig(method.def_id); let sig = fcx.normalize_associated_types_in(span, &sig); - let sig = fcx.liberate_late_bound_regions(method.def_id, &sig); + let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, &sig); debug!("check_method_receiver: sig={:?}", sig); let self_ty = fcx.normalize_associated_types_in(span, &self_ty); - let self_ty = fcx.liberate_late_bound_regions( + let self_ty = fcx.tcx.liberate_late_bound_regions( method.def_id, &ty::Binder(self_ty) ); @@ -498,7 +498,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver); let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty); - let self_arg_ty = fcx.liberate_late_bound_regions( + let self_arg_ty = fcx.tcx.liberate_late_bound_regions( method.def_id, &ty::Binder(self_arg_ty) ); From fa813f74a21ae21a4491667e7201319017f4093e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Nov 2017 11:18:40 -0500 Subject: [PATCH 19/30] introduce `closure_env_ty` helper to compute ty of closure env arg Previously the code was somewhat duplicated. --- src/librustc/ty/mod.rs | 6 ++++++ src/librustc/ty/util.rs | 27 +++++++++++++++++++++++++++ src/librustc_mir/build/mod.rs | 30 +++++++----------------------- src/librustc_trans/common.rs | 10 ++-------- 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 07573a48c03ee..afe999cede70d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1921,6 +1921,12 @@ impl<'a, 'gcx, 'tcx> FieldDef { } } +/// Represents the various closure traits in the Rust language. This +/// will determine the type of the environment (`self`, in the +/// desuaring) argument that the closure expects. +/// +/// You can get the environment type of a closure using +/// `tcx.closure_env_ty()`. #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum ClosureKind { // Warning: Ordering is significant here! The ordering is chosen diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 23dd3f1bc2bba..e19bab46402bf 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -634,6 +634,33 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { def_id } + /// Given the def-id and substs a closure, creates the type of + /// `self` argument that the closure expects. For example, for a + /// `Fn` closure, this would return a reference type `&T` where + /// `T=closure_ty`. + /// + /// Returns `None` if this closure's kind has not yet been inferred. + /// This should only be possible during type checking. + /// + /// Note that the return value is a late-bound region and hence + /// wrapped in a binder. + pub fn closure_env_ty(self, + closure_def_id: DefId, + closure_substs: ty::ClosureSubsts<'tcx>) + -> Option>> + { + let closure_ty = self.mk_closure(closure_def_id, closure_substs); + let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); + let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self); + let closure_kind = closure_kind_ty.to_opt_closure_kind()?; + let env_ty = match closure_kind { + ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty), + ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty), + ty::ClosureKind::FnOnce => closure_ty, + }; + Some(ty::Binder(env_ty)) + } + /// Given the def-id of some item that has no type parameters, make /// a suitable "empty substs" for it. pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> &'tcx ty::Substs<'tcx> { diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 4349820dbe9e1..d814b092c9d69 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -100,7 +100,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t // HACK(eddyb) Avoid having RustCall on closures, // as it adds unnecessary (and wrong) auto-tupling. abi = Abi::Rust; - Some((closure_self_ty(tcx, id, body_id), None)) + Some((liberated_closure_env_ty(tcx, id, body_id), None)) } ty::TyGenerator(..) => { let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id); @@ -246,10 +246,10 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from -pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - closure_expr_id: ast::NodeId, - body_id: hir::BodyId) - -> Ty<'tcx> { +fn liberated_closure_env_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + closure_expr_id: ast::NodeId, + body_id: hir::BodyId) + -> Ty<'tcx> { let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id); let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id); @@ -258,24 +258,8 @@ pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, _ => bug!("closure expr does not have closure type: {:?}", closure_ty) }; - let region = ty::ReFree(ty::FreeRegion { - scope: closure_def_id, - bound_region: ty::BoundRegion::BrEnv, - }); - let region = tcx.mk_region(region); - - match closure_substs.closure_kind_ty(closure_def_id, tcx).to_opt_closure_kind().unwrap() { - ty::ClosureKind::Fn => - tcx.mk_ref(region, - ty::TypeAndMut { ty: closure_ty, - mutbl: hir::MutImmutable }), - ty::ClosureKind::FnMut => - tcx.mk_ref(region, - ty::TypeAndMut { ty: closure_ty, - mutbl: hir::MutMutable }), - ty::ClosureKind::FnOnce => - closure_ty - } + let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap(); + tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty) } struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 1f92c1067845e..405647af324d6 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -395,15 +395,9 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let tcx = ccx.tcx(); let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs); - let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); - let env_ty = match substs.closure_kind(def_id, tcx) { - ty::ClosureKind::Fn => tcx.mk_imm_ref(tcx.mk_region(env_region), ty), - ty::ClosureKind::FnMut => tcx.mk_mut_ref(tcx.mk_region(env_region), ty), - ty::ClosureKind::FnOnce => ty, - }; - + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(env_ty).chain(sig.inputs().iter().cloned()), + iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), sig.output(), sig.variadic, sig.unsafety, From 243bf3f7180e1d35a0ed9934346345c83eeeb4c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Nov 2017 13:12:24 -0500 Subject: [PATCH 20/30] rename "free region" to "universally quantified region" This has been bugging me. All the regions appear free in the source; the real difference is that some of them are universally quantified (those in the function signature) and some are existentially quantified (those for which we are inferring values). --- src/librustc_mir/borrow_check/nll/mod.rs | 26 ++++--- .../borrow_check/nll/region_infer/mod.rs | 73 +++++++++++-------- src/librustc_mir/borrow_check/nll/renumber.rs | 20 ++--- .../nll/subtype_constraint_generation.rs | 12 +-- .../{free_regions.rs => universal_regions.rs} | 32 ++++---- 5 files changed, 88 insertions(+), 75 deletions(-) rename src/librustc_mir/borrow_check/nll/{free_regions.rs => universal_regions.rs} (73%) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index a04cc113e723d..804f5e2687597 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -26,8 +26,8 @@ use self::mir_util::PassWhere; mod constraint_generation; mod subtype_constraint_generation; -mod free_regions; -use self::free_regions::FreeRegions; +mod universal_regions; +use self::universal_regions::UniversalRegions; pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; @@ -42,14 +42,14 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, def_id: DefId, mir: &mut Mir<'tcx>, -) -> FreeRegions<'tcx> { +) -> UniversalRegions<'tcx> { // Compute named region information. - let free_regions = free_regions::free_regions(infcx, def_id); + let universal_regions = universal_regions::universal_regions(infcx, def_id); // Replace all regions with fresh inference variables. - renumber::renumber_mir(infcx, &free_regions, mir); + renumber::renumber_mir(infcx, &universal_regions, mir); - free_regions + universal_regions } /// Computes the (non-lexical) regions from the input MIR. @@ -58,7 +58,7 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, def_id: DefId, - free_regions: FreeRegions<'tcx>, + universal_regions: UniversalRegions<'tcx>, mir: &Mir<'tcx>, param_env: ty::ParamEnv<'gcx>, flow_inits: &mut FlowInProgress>, @@ -71,8 +71,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // 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); + let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir); + subtype_constraint_generation::generate( + &mut regioncx, + &universal_regions, + mir, + constraint_sets, + ); // Compute what is live where. let liveness = &LivenessResults { @@ -178,8 +183,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( writeln!(out, " | Live variables at {:?}: {}", location, s)?; } - PassWhere::AfterLocation(_) | - PassWhere::AfterCFG => {} + PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {} } Ok(()) }); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index b304ea4f7149a..26561329ae924 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::free_regions::FreeRegions; +use super::universal_regions::UniversalRegions; use rustc::infer::InferCtxt; use rustc::infer::RegionVariableOrigin; use rustc::infer::NLLRegionVariableOrigin; @@ -33,8 +33,8 @@ pub struct RegionInferenceContext<'tcx> { /// The liveness constraints added to each region. For most /// regions, these start out empty and steadily grow, though for - /// each free region R they start out containing the entire CFG - /// and `end(R)`. + /// each universally quantified region R they start out containing + /// the entire CFG and `end(R)`. /// /// In this `BitMatrix` representation, the rows are the region /// variables and the columns are the free regions and MIR locations. @@ -52,7 +52,10 @@ pub struct RegionInferenceContext<'tcx> { /// the free regions.) point_indices: BTreeMap, - num_free_regions: usize, + /// Number of universally quantified regions. This is used to + /// determine the meaning of the bits in `inferred_values` and + /// friends. + num_universal_regions: usize, free_region_map: &'tcx FreeRegionMap<'tcx>, } @@ -92,10 +95,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free - /// regions defined in `free_regions`. - pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self { + /// regions defined in `universal_regions`. + pub fn new( + var_origins: VarOrigins, + universal_regions: &UniversalRegions<'tcx>, + mir: &Mir<'tcx>, + ) -> Self { let num_region_variables = var_origins.len(); - let num_free_regions = free_regions.indices.len(); + let num_universal_regions = universal_regions.indices.len(); let mut num_points = 0; let mut point_indices = BTreeMap::new(); @@ -106,7 +113,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { block, statement_index, }; - point_indices.insert(location, num_free_regions + num_points); + point_indices.insert(location, num_universal_regions + num_points); num_points += 1; } } @@ -121,25 +128,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { definitions, liveness_constraints: BitMatrix::new( num_region_variables, - num_free_regions + num_points, + num_universal_regions + num_points, ), inferred_values: None, constraints: Vec::new(), point_indices, - num_free_regions, - free_region_map: free_regions.free_region_map, + num_universal_regions, + free_region_map: universal_regions.free_region_map, }; - result.init_free_regions(free_regions); + result.init_universal_regions(universal_regions); result } - /// Initializes the region variables for each free region - /// (lifetime parameter). The first N variables always correspond - /// to the free regions appearing in the function signature (both - /// named and anonymous) and where clauses. This function iterates - /// over those regions and initializes them with minimum values. + /// Initializes the region variables for each universally + /// quantified region (lifetime parameter). The first N variables + /// always correspond to the regions appearing in the function + /// signature (both named and anonymous) and where clauses. This + /// function iterates over those regions and initializes them with + /// minimum values. /// /// For example: /// @@ -154,13 +162,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// and (b) any free regions that it outlives, which in this case /// is just itself. R1 (`'b`) in contrast also outlives `'a` and /// hence contains R0 and R1. - fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>) { - let FreeRegions { + fn init_universal_regions(&mut self, universal_regions: &UniversalRegions<'tcx>) { + let UniversalRegions { indices, free_region_map: _, - } = free_regions; + } = universal_regions; - // For each free region X: + // For each universally quantified region X: for (free_region, &variable) in indices { // These should be free-region variables. assert!(match self.definitions[variable].origin { @@ -218,7 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, matrix: &BitMatrix, r: RegionVid, - s: RegionVid + s: RegionVid, ) -> bool { matrix.contains(r.index(), s.index()) } @@ -240,7 +248,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - for fr in (0 .. self.num_free_regions).map(RegionVid::new) { + for fr in (0..self.num_universal_regions).map(RegionVid::new) { if self.region_contains_region_in_matrix(inferred_values, r, fr) { result.push_str(&format!("{}{:?}", sep, fr)); sep = ", "; @@ -287,9 +295,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Find the minimal regions that can solve the constraints. This is infallible. self.propagate_constraints(mir); - // Now, see whether any of the constraints were too strong. In particular, - // we want to check for a case where a free region exceeded its bounds. - // Consider: + // Now, see whether any of the constraints were too strong. In + // particular, we want to check for a case where a universally + // quantified region exceeded its bounds. Consider: // // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } // @@ -300,7 +308,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // have no evidence that `'b` outlives `'a`, so we want to report // an error. - // The free regions are always found in a prefix of the full list. + // The universal regions are always found in a prefix of the + // full list. let free_region_definitions = self.definitions .iter_enumerated() .take_while(|(_, fr_definition)| fr_definition.name.is_some()); @@ -322,7 +331,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). - for outlived_fr in fr_value.take_while(|&i| i < self.num_free_regions) { + for outlived_fr in fr_value.take_while(|&i| i < self.num_universal_regions) { // `fr` includes `end(fr)`, that's not especially // interesting. if fr.index() == outlived_fr { @@ -451,11 +460,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If we reach the END point in the graph, then copy // over any skolemized end points in the `from_region` // and make sure they are included in the `to_region`. - let free_region_indices = inferred_values + let universal_region_indices = inferred_values .iter(from_region.index()) - .take_while(|&i| i < self.num_free_regions) + .take_while(|&i| i < self.num_universal_regions) .collect::>(); - for fr in &free_region_indices { + for fr in &universal_region_indices { changed |= inferred_values.add(to_region.index(), *fr); } } else { @@ -523,7 +532,7 @@ impl<'tcx> RegionDefinition<'tcx> { fn new(origin: RegionVariableOrigin) -> Self { // Create a new region definition. Note that, for free // regions, these fields get updated later in - // `init_free_regions`. + // `init_universal_regions`. Self { origin, name: None } } } diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 1076b774de657..371419da02448 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -16,18 +16,18 @@ use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; use super::ToRegionVid; -use super::free_regions::FreeRegions; +use super::universal_regions::UniversalRegions; /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. pub fn renumber_mir<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, - free_regions: &FreeRegions<'tcx>, + universal_regions: &UniversalRegions<'tcx>, mir: &mut Mir<'tcx>, ) { // Create inference variables for each of the free regions // declared on the function signature. - let free_region_inference_vars = (0..free_regions.indices.len()) + let free_region_inference_vars = (0..universal_regions.indices.len()) .map(RegionVid::new) .map(|vid_expected| { let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion); @@ -37,12 +37,12 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( .collect(); debug!("renumber_mir()"); - debug!("renumber_mir: free_regions={:#?}", free_regions); + debug!("renumber_mir: universal_regions={:#?}", universal_regions); debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); let mut visitor = NLLVisitor { infcx, - free_regions, + universal_regions, free_region_inference_vars, arg_count: mir.arg_count, }; @@ -51,7 +51,7 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - free_regions: &'a FreeRegions<'tcx>, + universal_regions: &'a UniversalRegions<'tcx>, free_region_inference_vars: IndexVec>, arg_count: usize, } @@ -76,16 +76,16 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { /// Renumbers the regions appearing in `value`, but those regions /// are expected to be free regions from the function signature. - fn renumber_free_regions(&mut self, value: &T) -> T + fn renumber_universal_regions(&mut self, value: &T) -> T where T: TypeFoldable<'tcx>, { - debug!("renumber_free_regions(value={:?})", value); + debug!("renumber_universal_regions(value={:?})", value); self.infcx .tcx .fold_regions(value, &mut false, |region, _depth| { - let index = self.free_regions.indices[®ion]; + let index = self.universal_regions.indices[®ion]; self.free_region_inference_vars[index] }) } @@ -112,7 +112,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { let old_ty = *ty; *ty = if is_arg { - self.renumber_free_regions(&old_ty) + self.renumber_universal_regions(&old_ty) } else { self.renumber_regions(ty_context, &old_ty) }; diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index c1850c76541d7..dbae40be6fe1d 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -15,7 +15,7 @@ use rustc::ty; use transform::type_check::MirTypeckRegionConstraints; use transform::type_check::OutlivesSet; -use super::free_regions::FreeRegions; +use super::universal_regions::UniversalRegions; use super::region_infer::RegionInferenceContext; /// When the MIR type-checker executes, it validates all the types in @@ -25,20 +25,20 @@ use super::region_infer::RegionInferenceContext; /// them into the NLL `RegionInferenceContext`. pub(super) fn generate<'tcx>( regioncx: &mut RegionInferenceContext<'tcx>, - free_regions: &FreeRegions<'tcx>, + universal_regions: &UniversalRegions<'tcx>, mir: &Mir<'tcx>, constraints: &MirTypeckRegionConstraints<'tcx>, ) { SubtypeConstraintGenerator { regioncx, - free_regions, + universal_regions, mir, }.generate(constraints); } struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { regioncx: &'cx mut RegionInferenceContext<'tcx>, - free_regions: &'cx FreeRegions<'tcx>, + universal_regions: &'cx UniversalRegions<'tcx>, mir: &'cx Mir<'tcx>, } @@ -102,11 +102,11 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { // 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*. + // the set of universal regions *somewhere*. if let ty::ReVar(vid) = r { *vid } else { - self.free_regions.indices[&r] + self.universal_regions.indices[&r] } } } diff --git a/src/librustc_mir/borrow_check/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs similarity index 73% rename from src/librustc_mir/borrow_check/nll/free_regions.rs rename to src/librustc_mir/borrow_check/nll/universal_regions.rs index 7f984c72ee615..3be95a114c3bc 100644 --- a/src/librustc_mir/borrow_check/nll/free_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Code to extract the free regions declared on a function and the -//! relationships between them. For example: +//! Code to extract the universally quantified regions declared on a +//! function and the relationships between them. For example: //! //! ``` //! fn foo<'a, 'b, 'c: 'b>() { } @@ -31,22 +31,22 @@ use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::Idx; #[derive(Debug)] -pub struct FreeRegions<'tcx> { - /// Given a free region defined on this function (either early- or - /// late-bound), this maps it to its internal region index. When - /// the region context is created, the first N variables will be - /// created based on these indices. +pub struct UniversalRegions<'tcx> { + /// Given a universally quantified region defined on this function + /// (either early- or late-bound), this maps it to its internal + /// region index. When the region context is created, the first N + /// variables will be created based on these indices. pub indices: FxHashMap, RegionVid>, - /// The map from the typeck tables telling us how to relate free regions. + /// The map from the typeck tables telling us how to relate universal regions. pub free_region_map: &'tcx FreeRegionMap<'tcx>, } -pub fn free_regions<'a, 'gcx, 'tcx>( +pub fn universal_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, item_def_id: DefId, -) -> FreeRegions<'tcx> { - debug!("free_regions(item_def_id={:?})", item_def_id); +) -> UniversalRegions<'tcx> { + debug!("universal_regions(item_def_id={:?})", item_def_id); let mut indices = FxHashMap(); @@ -76,15 +76,15 @@ pub fn free_regions<'a, 'gcx, 'tcx>( } }); - debug!("free_regions: indices={:#?}", indices); + debug!("universal_regions: indices={:#?}", indices); - FreeRegions { indices, free_region_map: &tables.free_region_map } + UniversalRegions { indices, free_region_map: &tables.free_region_map } } fn insert_free_region<'tcx>( - free_regions: &mut FxHashMap, RegionVid>, + universal_regions: &mut FxHashMap, RegionVid>, region: ty::Region<'tcx>, ) { - let next = RegionVid::new(free_regions.len()); - free_regions.entry(region).or_insert(next); + let next = RegionVid::new(universal_regions.len()); + universal_regions.entry(region).or_insert(next); } From 2e63bb34199545042c049f67be3e3253520a85fc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 16:52:29 -0500 Subject: [PATCH 21/30] fix debruijn account in `for_each_free_region` --- src/librustc/ty/fold.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index fbbc0e92bcdba..069dc0275cbb1 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -247,7 +247,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { match *r { - ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => { + ty::ReLateBound(debruijn, _) if debruijn.depth <= self.current_depth => { /* ignore bound regions */ } _ => (self.callback)(r), From 18cc0f9f374e4dcd6e933164037654b39e841148 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 16:53:17 -0500 Subject: [PATCH 22/30] document `closure_base_def_id` --- src/librustc/ty/util.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index e19bab46402bf..129badc46d8c1 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -624,6 +624,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr } + /// Given the `DefId` of a fn or closure, returns the `DefId` of + /// the innermost fn item that the closure is contained within. + /// This is a significant def-id because, when we do + /// type-checking, we type-check this fn item and all of its + /// (transitive) closures together. Therefore, when we fetch the + /// `typeck_tables_of` the closure, for example, we really wind up + /// fetching the `typeck_tables_of` the enclosing fn item. pub fn closure_base_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; while self.is_closure(def_id) { From 506e80c8998d299b61b7e75b8e6cb405bac71d9e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 17:30:34 -0500 Subject: [PATCH 23/30] remove unused span from `eq_types` (and rustfmt slightly) --- src/librustc_mir/transform/type_check.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index fc2a51e273819..45594613fa43d 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -178,9 +178,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let sty = self.sanitize_type(place, sty); let ty = self.tcx().type_of(def_id); let ty = self.cx.normalize(&ty, location); - if let Err(terr) = self.cx - .eq_types(self.last_span, ty, sty, location.at_self()) - { + if let Err(terr) = self.cx.eq_types(ty, sty, location.at_self()) { span_mirbug!( self, place, @@ -230,7 +228,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); - let span = self.last_span; match *pi { ProjectionElem::Deref => { let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); @@ -316,7 +313,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let fty = self.sanitize_type(place, fty); match self.field_ty(place, base, field, location) { Ok(ty) => { - if let Err(terr) = self.cx.eq_types(span, ty, fty, location.at_self()) { + if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) { span_mirbug!( self, place, @@ -529,13 +526,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }) } - fn eq_types( - &mut self, - _span: Span, - a: Ty<'tcx>, - b: Ty<'tcx>, - locations: Locations, - ) -> UnitResult<'tcx> { + fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { self.fully_perform_op(locations, |this| { this.infcx .at(&this.misc(this.last_span), this.param_env) From c1708abb071eb1a627ebadc494ed4b1083e44f2f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 1 Dec 2017 21:24:08 -0500 Subject: [PATCH 24/30] type_check.rs: rustfmt --- src/librustc_mir/transform/type_check.rs | 52 +++++++++++------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 45594613fa43d..f0b62e28a0da6 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -104,12 +104,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn visit_place( - &mut self, - place: &Place<'tcx>, - context: PlaceContext, - location: Location, - ) { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { self.sanitize_place(place, location, context); } @@ -164,11 +159,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn sanitize_place(&mut self, - place: &Place<'tcx>, - location: Location, - context: PlaceContext) - -> PlaceTy<'tcx> { + fn sanitize_place( + &mut self, + place: &Place<'tcx>, + location: Location, + context: PlaceContext, + ) -> PlaceTy<'tcx> { debug!("sanitize_place: {:?}", place); let place_ty = match *place { Place::Local(index) => PlaceTy::Ty { @@ -210,9 +206,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { }; if let PlaceContext::Copy = context { let ty = place_ty.to_ty(self.tcx()); - if self.cx.infcx.type_moves_by_default(self.cx.param_env, ty, DUMMY_SP) { - span_mirbug!(self, place, - "attempted copy of non-Copy type ({:?})", ty); + if self.cx + .infcx + .type_moves_by_default(self.cx.param_env, ty, DUMMY_SP) + { + span_mirbug!(self, place, "attempted copy of non-Copy type ({:?})", ty); } } place_ty @@ -312,18 +310,16 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(place, fty); match self.field_ty(place, base, field, location) { - Ok(ty) => { - if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) { - span_mirbug!( - self, - place, - "bad field access ({:?}: {:?}): {:?}", - ty, - fty, - terr - ); - } - } + Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) { + span_mirbug!( + self, + place, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); + }, Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( self, place, @@ -358,9 +354,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { variant_index, } => (&adt_def.variants[variant_index], substs), PlaceTy::Ty { ty } => match ty.sty { - ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => { - (&adt_def.variants[0], substs) - } + ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => (&adt_def.variants[0], substs), ty::TyClosure(def_id, substs) => { return match substs.upvar_tys(def_id, tcx).nth(field.index()) { Some(ty) => Ok(ty), From 14480b401a722529cd80303579dcfd8965442b11 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 10:36:22 -0500 Subject: [PATCH 25/30] free_region_map: Fix typo in comment: r_a <= r_b --- src/librustc/infer/outlives/free_region_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs index 72db8622672f8..2127c4714aef0 100644 --- a/src/librustc/infer/outlives/free_region_map.rs +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -38,7 +38,7 @@ impl<'tcx> FreeRegionMap<'tcx> { } } - /// Tests whether `r_a <= sup`. Both must be free regions or + /// Tests whether `r_a <= r_b`. Both must be free regions or /// `'static`. pub fn sub_free_regions<'a, 'gcx>(&self, r_a: Region<'tcx>, From a3935961f025dcd3bc8c3470ceea219754b11674 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 10:36:54 -0500 Subject: [PATCH 26/30] region_infer: Fix typo in comment --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 26561329ae924..be3278cdd9045 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -305,7 +305,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // and hence we establish (transitively) a constraint that // `'a: 'b`. The `propagate_constraints` code above will // therefore add `end('a)` into the region for `'b` -- but we - // have no evidence that `'b` outlives `'a`, so we want to report + // have no evidence that `'a` outlives `'b`, so we want to report // an error. // The universal regions are always found in a prefix of the From 7409ffd17e111bf0265b7a203642dfd98c57d24b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 10:42:46 -0500 Subject: [PATCH 27/30] outlives/env: Fix comment that lost surrounding context. --- src/librustc/infer/outlives/env.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index f7d94b47d7dfe..85e269b697293 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -162,9 +162,8 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { ) where I: IntoIterator>, { - // But also record other relationships, such as `T:'x`, - // that don't go into the free-region-map but which we use - // here. + // Record relationships such as `T:'x` that don't go into the + // free-region-map but which we use here. for outlives_bound in outlives_bounds { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { From 271616088f42eb6f1ccd78d2a9d7923507d04655 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 10:46:35 -0500 Subject: [PATCH 28/30] transtive_relation: fix typo in comment for `parents` --- src/librustc_data_structures/transitive_relation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index ffbb218dabedf..ba7ab0c07c66a 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -303,7 +303,7 @@ impl TransitiveRelation { /// Given an element A, returns the maximal set {B} of elements B /// such that /// - /// - A != A + /// - A != B /// - A R B is true /// - for each i, j: B[i] R B[j] does not hold /// From ab2bc9f073e15dd711c47102f42174740ce8f9b2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 10:54:38 -0500 Subject: [PATCH 29/30] outlives/env: fix comment, say must and not should --- src/librustc/infer/outlives/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 85e269b697293..d47507592f80d 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -152,7 +152,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { /// Processes outlives bounds that are known to hold, whether from implied or other sources. /// /// The `infcx` parameter is optional; if the implied bounds may - /// contain inference variables, it should be supplied, in which + /// contain inference variables, it must be supplied, in which /// case we will register "givens" on the inference context. (See /// `RegionConstraintData`.) fn add_outlives_bounds( From a6adc74e8726dd0fa0260964d0d7c03e9b48c655 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 10:49:40 -0500 Subject: [PATCH 30/30] adopt `longer` and `shorter` rather than `fr` and `outlived_fr` --- .../borrow_check/nll/region_infer/mod.rs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index be3278cdd9045..d1faaf75a5323 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -322,37 +322,39 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_free_region( &self, infcx: &InferCtxt<'_, '_, 'tcx>, - fr: RegionVid, - fr_definition: &RegionDefinition<'tcx>, + longer_fr: RegionVid, + longer_definition: &RegionDefinition<'tcx>, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); - let fr_name = fr_definition.name.unwrap(); - let fr_value = inferred_values.iter(fr.index()); + let longer_name = longer_definition.name.unwrap(); + let longer_value = inferred_values.iter(longer_fr.index()); + + // Find every region `shorter` such that `longer: shorter` + // (because `longer` includes `end(shorter)`). + for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) { + let shorter_fr = RegionVid::new(shorter_fr); - // Find every region `o` such that `fr: o` - // (because `fr` includes `end(o)`). - for outlived_fr in fr_value.take_while(|&i| i < self.num_universal_regions) { // `fr` includes `end(fr)`, that's not especially // interesting. - if fr.index() == outlived_fr { + if longer_fr == shorter_fr { continue; } - let outlived_fr_definition = &self.definitions[RegionVid::new(outlived_fr)]; - let outlived_fr_name = outlived_fr_definition.name.unwrap(); + let shorter_definition = &self.definitions[shorter_fr]; + let shorter_name = shorter_definition.name.unwrap(); // Check that `o <= fr`. If not, report an error. if !self.free_region_map - .sub_free_regions(outlived_fr_name, fr_name) + .sub_free_regions(shorter_name, longer_name) { // FIXME: worst error msg ever - let blame_span = self.blame_span(fr, RegionVid::new(outlived_fr)); + let blame_span = self.blame_span(longer_fr, shorter_fr); infcx.tcx.sess.span_err( blame_span, &format!( "free region `{}` does not outlive `{}`", - fr_name, - outlived_fr_name + longer_name, + shorter_name ), ); }