From 86334c711648cc17bba9955f955a6710159bd6f7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 05:29:37 -0500 Subject: [PATCH 01/25] remove unnecessary intermediate vector from `copy` --- .../borrow_check/nll/region_infer/mod.rs | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 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 c926c7432bb00..dfa7ce8687562 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -471,35 +471,31 @@ impl<'tcx> RegionInferenceContext<'tcx> { changed |= new; let block_data = &mir[p.block]; - let successor_points = if p.statement_index < block_data.statements.len() { - vec![ - Location { - statement_index: p.statement_index + 1, - ..p - }, - ] + + let start_stack_len = stack.len(); + + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); } else { - block_data - .terminator() - .successors() - .iter() - .map(|&basic_block| { + stack.extend(block_data.terminator().successors().iter().map( + |&basic_block| { Location { statement_index: 0, block: basic_block, } - }) - .collect::>() - }; + }, + )); + } - if successor_points.is_empty() { + if stack.len() == start_stack_len { // 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`. changed |= inferred_values.add_universal_regions_outlived_by(from_region, to_region); - } else { - stack.extend(successor_points); } } From a0f0392a6d20dda6fcf7169a81688a0f8e1b51da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 05:49:27 -0500 Subject: [PATCH 02/25] rename `copy` to `dfs` and make it customizable --- .../borrow_check/nll/region_infer/dfs.rs | 217 ++++++++++++++++++ .../borrow_check/nll/region_infer/mod.rs | 83 ++----- 2 files changed, 230 insertions(+), 70 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/region_infer/dfs.rs diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs new file mode 100644 index 0000000000000..c8ac0fa233ffa --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -0,0 +1,217 @@ +// 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. + +//! Module defining the `dfs` method on `RegionInferenceContext`, along with +//! its associated helper traits. + +use rustc::mir::{Location, Mir}; +use rustc::ty::RegionVid; +use rustc_data_structures::fx::FxHashSet; +use super::RegionInferenceContext; +use super::values::{RegionElementIndex, RegionValues, RegionValueElements}; + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Function used to satisfy or test a `R1: R2 @ P` + /// constraint. The core idea is that it performs a DFS starting + /// from `P`. The precise actions *during* that DFS depend on the + /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more + /// details. + /// + /// Returns: + /// + /// - `Ok(true)` if the walk was completed and something changed + /// along the way; + /// - `Ok(false)` if the walk was completed with no changes; + /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the + /// value that `op` returned. + pub(super) fn dfs(&self, mir: &Mir<'tcx>, mut op: C) -> Result + where + C: DfsOp, + { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(op.start_point()); + while let Some(p) = stack.pop() { + let point_index = self.elements.index(p); + + if !op.source_region_contains(point_index) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + debug!(" already visited"); + continue; + } + + let new = op.add_to_target_region(point_index)?; + changed |= new; + + let block_data = &mir[p.block]; + + let start_stack_len = stack.len(); + + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); + } else { + stack.extend(block_data.terminator().successors().iter().map( + |&basic_block| { + Location { + statement_index: 0, + block: basic_block, + } + }, + )); + } + + if stack.len() == start_stack_len { + // 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`. + changed |= op.add_universal_regions_outlived_by_source_to_target()?; + } + } + + Ok(changed) + } +} + +/// Customizes the operation of the `dfs` function. This function is +/// used during inference to satisfy a `R1: R2 @ P` constraint. +pub(super) trait DfsOp { + /// If this op stops the walk early, what type does it propagate? + type Early; + + /// Returns the point from which to start the DFS. + fn start_point(&self) -> Location; + + /// Returns true if the source region contains the given point. + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool; + + /// Adds the given point to the target region, returning true if + /// something has changed. Returns `Err` if we should abort the + /// walk early. + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result; + + /// Adds all universal regions in the source region to the target region, returning + /// true if something has changed. + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result; +} + +/// Used during inference to enforce a `R1: R2 @ P` constraint. For +/// each point Q we reach along the DFS, we check if Q is in R2 (the +/// "source region"). If not, we stop the walk. Otherwise, we add Q to +/// R1 (the "target region") and continue to Q's successors. If we +/// reach the end of the graph, then we add any universal regions from +/// R2 into R1. +pub(super) struct CopyFromSourceToTarget<'v> { + pub source_region: RegionVid, + pub target_region: RegionVid, + pub inferred_values: &'v mut RegionValues, + pub constraint_point: Location, +} + +impl<'v> DfsOp for CopyFromSourceToTarget<'v> { + /// We never stop the walk early. + type Early = !; + + fn start_point(&self) -> Location { + self.constraint_point + } + + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { + self.inferred_values + .contains(self.source_region, point_index) + } + + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result { + Ok(self.inferred_values.add(self.target_region, point_index)) + } + + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result { + Ok( + self.inferred_values + .add_universal_regions_outlived_by(self.source_region, self.target_region), + ) + } +} + +/// Used after inference to *test* a `R1: R2 @ P` constraint. For +/// each point Q we reach along the DFS, we check if Q in R2 is also +/// contained in R1. If not, we abort the walk early with an `Err` +/// condition. Similarly, if we reach the end of the graph and find +/// that R1 contains some universal region that R2 does not contain, +/// we abort the walk early. +#[allow(dead_code)] // TODO +pub(super) struct TestTarget<'v> { + source_region: RegionVid, + target_region: RegionVid, + elements: &'v RegionValueElements, + inferred_values: &'v RegionValues, + constraint_point: Location, +} + +#[allow(dead_code)] // TODO +impl<'v> DfsOp for TestTarget<'v> { + /// The element that was not found within R2. + type Early = RegionElementIndex; + + fn start_point(&self) -> Location { + self.constraint_point + } + + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { + self.inferred_values + .contains(self.source_region, point_index) + } + + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result { + if !self.inferred_values + .contains(self.target_region, point_index) + { + return Err(point_index); + } + + Ok(false) + } + + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result { + for ur in self.inferred_values + .universal_regions_outlived_by(self.source_region) + { + if !self.inferred_values.contains(self.target_region, ur) { + return Err(self.elements.index(ur)); + } + } + + Ok(false) + } +} 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 dfa7ce8687562..6d76398b941ed 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -18,12 +18,13 @@ use rustc::infer::region_constraints::VarOrigins; use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::fx::FxHashSet; use std::fmt; use std::rc::Rc; use syntax_pos::Span; mod annotation; +mod dfs; +use self::dfs::CopyFromSourceToTarget; mod dump_mir; mod graphviz; mod values; @@ -421,14 +422,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Grow the value as needed to accommodate the // outlives constraint. - - if self.copy( - &mut inferred_values, + let Ok(made_changes) = self.dfs( mir, - constraint.sub, - constraint.sup, - constraint.point, - ) { + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + }, + ); + + if made_changes { debug!("propagate_constraints: sub={:?}", constraint.sub); debug!("propagate_constraints: sup={:?}", constraint.sup); changed = true; @@ -440,68 +444,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } - fn copy( - &self, - inferred_values: &mut RegionValues, - mir: &Mir<'tcx>, - from_region: RegionVid, - to_region: RegionVid, - constraint_point: Location, - ) -> bool { - let mut changed = false; - - let mut stack = vec![]; - let mut visited = FxHashSet(); - - stack.push(constraint_point); - while let Some(p) = stack.pop() { - let point_index = self.elements.index(p); - - if !inferred_values.contains(from_region, point_index) { - debug!(" not in from-region"); - continue; - } - - if !visited.insert(p) { - debug!(" already visited"); - continue; - } - - let new = inferred_values.add(to_region, point_index); - changed |= new; - - let block_data = &mir[p.block]; - - let start_stack_len = stack.len(); - - if p.statement_index < block_data.statements.len() { - stack.push(Location { - statement_index: p.statement_index + 1, - ..p - }); - } else { - stack.extend(block_data.terminator().successors().iter().map( - |&basic_block| { - Location { - statement_index: 0, - block: basic_block, - } - }, - )); - } - - if stack.len() == start_stack_len { - // 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`. - changed |= - inferred_values.add_universal_regions_outlived_by(from_region, to_region); - } - } - - 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 { @@ -647,3 +589,4 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { } } } + From cd564d20ff521d56ad3f01fe0e0588d9487c0c13 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 06:16:52 -0500 Subject: [PATCH 03/25] only propagate ClosureRegionRequirements if non-trivial Before, we would always have a `Some` ClosureRegionRequirements if we were inferring values for a closure. Now we only do is it has a non-empty set of outlives requirements. --- .../borrow_check/nll/region_infer/mod.rs | 173 ++++++++++-------- .../escape-argument-callee.stderr | 3 +- .../escape-argument.stderr | 3 +- ...pagate-approximated-fail-no-postdom.stderr | 3 +- ...er-to-static-comparing-against-free.stderr | 3 +- .../propagate-approximated-to-empty.stderr | 45 +++++ ...ail-to-approximate-longer-no-bounds.stderr | 7 +- ...-to-approximate-longer-wrong-bounds.stderr | 7 +- .../return-wrong-bound-region.stderr | 3 +- 9 files changed, 148 insertions(+), 99 deletions(-) create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr 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 6d76398b941ed..af88edc22cee3 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -251,24 +251,88 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, ) -> Option { assert!(self.inferred_values.is_none(), "values already inferred"); - let tcx = infcx.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 universally - // quantified 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 `'a` outlives `'b`, so we want to report - // an error. + let outlives_requirements = self.check_universal_regions(infcx, mir_def_id); + + if outlives_requirements.is_empty() { + None + } else { + let num_external_vids = self.universal_regions.num_global_and_external_regions(); + Some(ClosureRegionRequirements { + num_external_vids, + outlives_requirements, + }) + } + } + /// Propagate the region constraints: this will grow the values + /// 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>) { + let mut changed = true; + + debug!("propagate_constraints()"); + debug!("propagate_constraints: constraints={:#?}", { + let mut constraints: Vec<_> = self.constraints.iter().collect(); + constraints.sort(); + 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; + debug!("propagate_constraints: --------------------"); + for constraint in &self.constraints { + debug!("propagate_constraints: constraint={:?}", constraint); + + // Grow the value as needed to accommodate the + // outlives constraint. + let Ok(made_changes) = self.dfs( + mir, + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + }, + ); + + if made_changes { + debug!("propagate_constraints: sub={:?}", constraint.sub); + debug!("propagate_constraints: sup={:?}", constraint.sup); + changed = true; + } + } + debug!("\n"); + } + + self.inferred_values = Some(inferred_values); + } + + /// Once regions have been propagated, this method is used to 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 } + /// + /// 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. + fn check_universal_regions( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + mir_def_id: DefId, + ) -> Vec { // The universal regions are always found in a prefix of the // full list. let universal_definitions = self.definitions @@ -283,27 +347,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_universal_region(infcx, fr, &mut outlives_requirements); } - // If this is not a closure, then there is no caller to which we can - // "pass the buck". So if there are any outlives-requirements that were - // not satisfied, we just have to report a hard error here. - if !tcx.is_closure(mir_def_id) { - for outlives_requirement in outlives_requirements { - self.report_error( - infcx, - outlives_requirement.free_region, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); - } - return None; + // If this is a closure, we can propagate unsatisfied + // `outlives_requirements` to our creator. Otherwise, we have + // to report a hard error here. + if infcx.tcx.is_closure(mir_def_id) { + return outlives_requirements; } - let num_external_vids = self.universal_regions.num_global_and_external_regions(); + for outlives_requirement in outlives_requirements { + self.report_error( + infcx, + outlives_requirement.free_region, + outlives_requirement.outlived_free_region, + outlives_requirement.blame_span, + ); + } - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements, - }) + vec![] } /// Check the final value for the free region `fr` to see if it @@ -396,54 +456,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } - /// Propagate the region constraints: this will grow the values - /// 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>) { - let mut changed = true; - - debug!("propagate_constraints()"); - debug!("propagate_constraints: constraints={:#?}", { - let mut constraints: Vec<_> = self.constraints.iter().collect(); - constraints.sort(); - 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; - debug!("propagate_constraints: --------------------"); - for constraint in &self.constraints { - debug!("propagate_constraints: constraint={:?}", constraint); - - // Grow the value as needed to accommodate the - // outlives constraint. - let Ok(made_changes) = self.dfs( - mir, - CopyFromSourceToTarget { - source_region: constraint.sub, - target_region: constraint.sup, - inferred_values: &mut inferred_values, - constraint_point: constraint.point, - }, - ); - - if made_changes { - debug!("propagate_constraints: sub={:?}", constraint.sub); - debug!("propagate_constraints: sup={:?}", constraint.sup); - changed = true; - } - } - debug!("\n"); - } - - 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 { @@ -589,4 +601,3 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { } } } - diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index c842d51a2ad74..2dfafd8f1725b 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -10,7 +10,7 @@ error: free region `'_#4r` does not outlive free region `'_#3r` 36 | let mut closure = expect_sig(|p, y| *p = y); | ^^^^^^ -note: External requirements +note: No external requirements --> $DIR/escape-argument-callee.rs:36:38 | 36 | let mut closure = expect_sig(|p, y| *p = y); @@ -20,7 +20,6 @@ note: External requirements i16, for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) i32)) ] - = note: number of external vids: 1 note: No external requirements --> $DIR/escape-argument-callee.rs:30:1 diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 0ec671997e7af..567ed299ac150 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -1,4 +1,4 @@ -note: External requirements +note: No external requirements --> $DIR/escape-argument.rs:36:38 | 36 | let mut closure = expect_sig(|p, y| *p = y); @@ -8,7 +8,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) ] - = note: number of external vids: 1 note: No external requirements --> $DIR/escape-argument.rs:30:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index d581622c4c63e..cdda8ab5392bd 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -10,7 +10,7 @@ error: free region `'_#5r` does not outlive free region `'_#6r` 57 | demand_y(x, y, p) | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9 | 53 | / |_outlives1, _outlives2, _outlives3, x, y| { @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) ] - = note: number of external vids: 4 note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:48:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index b93c69dc13f46..3e54e62d0110f 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -10,7 +10,7 @@ error: free region `'_#2r` does not outlive free region `'_#1r` 33 | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure | ^^^^^^ -note: External requirements +note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15 | 31 | foo(cell, |cell_a, cell_x| { @@ -25,7 +25,6 @@ note: External requirements i32, for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>)) ] - = note: number of external vids: 2 note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:28:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr new file mode 100644 index 0000000000000..e8dc8a13f876b --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -0,0 +1,45 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-approximated-to-empty.rs:41:9 + | +41 | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `'_#6r` does not outlive free region `'_#4r` + --> $DIR/propagate-approximated-to-empty.rs:41:21 + | +41 | demand_y(x, y, x.get()) + | ^ + +note: No external requirements + --> $DIR/propagate-approximated-to-empty.rs:39:47 + | +39 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | _______________________________________________^ +40 | | // Only works if 'x: 'y: +41 | | demand_y(x, y, x.get()) +42 | | //~^ WARN not reporting region error due to -Znll +43 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r` +44 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[317d]::supply[0]::{{closure}}[0]) with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) + ] + +note: No external requirements + --> $DIR/propagate-approximated-to-empty.rs:38:1 + | +38 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +39 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +40 | | // Only works if 'x: 'y: +41 | | demand_y(x, y, x.get()) +... | +44 | | }); +45 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_approximated_to_empty[317d]::supply[0]) with substs [] + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 6094f9aad81da..f3c40c838fb3e 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#6r` does not outlive free region `'_#4r` - --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21 + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:18 | 47 | demand_y(x, y, x.get()) - | ^ + | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47 | 45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) ] - = note: number of external vids: 2 note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:44:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 6658ee63abd9d..a66c2a7897024 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#5r` does not outlive free region `'_#7r` - --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21 + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:18 | 51 | demand_y(x, y, x.get()) - | ^ + | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47 | 49 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>)) ] - = note: number of external vids: 3 note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:48:1 diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index 8999f69e8ded6..cb2b2e2f11860 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -10,7 +10,7 @@ error: free region `'_#3r` does not outlive free region `'_#2r` 21 | expect_sig(|a, b| b); // ought to return `a` | ^ -note: External requirements +note: No external requirements --> $DIR/return-wrong-bound-region.rs:21:16 | 21 | expect_sig(|a, b| b); // ought to return `a` @@ -20,7 +20,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32 ] - = note: number of external vids: 1 note: No external requirements --> $DIR/return-wrong-bound-region.rs:20:1 From 6193c5cc2acd1c427a372dfdab3d071e880ecf62 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 07:01:09 -0500 Subject: [PATCH 04/25] translate `Verify`s into `TypeTest`s and check them --- .../borrow_check/nll/region_infer/dfs.rs | 48 ++-- .../borrow_check/nll/region_infer/mod.rs | 220 +++++++++++++++++- .../nll/subtype_constraint_generation.rs | 68 +++++- src/test/ui/nll/ty-outlives/ty-param-fn.rs | 51 ++++ .../ui/nll/ty-outlives/ty-param-fn.stderr | 26 +++ 5 files changed, 389 insertions(+), 24 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn.stderr diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs index c8ac0fa233ffa..dcf5d979a4c0a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -11,11 +11,12 @@ //! Module defining the `dfs` method on `RegionInferenceContext`, along with //! its associated helper traits. +use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValues, RegionValueElements}; use rustc::mir::{Location, Mir}; use rustc::ty::RegionVid; use rustc_data_structures::fx::FxHashSet; -use super::RegionInferenceContext; -use super::values::{RegionElementIndex, RegionValues, RegionValueElements}; impl<'tcx> RegionInferenceContext<'tcx> { /// Function used to satisfy or test a `R1: R2 @ P` @@ -165,17 +166,16 @@ impl<'v> DfsOp for CopyFromSourceToTarget<'v> { /// condition. Similarly, if we reach the end of the graph and find /// that R1 contains some universal region that R2 does not contain, /// we abort the walk early. -#[allow(dead_code)] // TODO -pub(super) struct TestTarget<'v> { - source_region: RegionVid, - target_region: RegionVid, - elements: &'v RegionValueElements, - inferred_values: &'v RegionValues, - constraint_point: Location, +pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> { + pub source_region: RegionVid, + pub target_region: RegionVid, + pub elements: &'v RegionValueElements, + pub universal_regions: &'v UniversalRegions<'tcx>, + pub inferred_values: &'v RegionValues, + pub constraint_point: Location, } -#[allow(dead_code)] // TODO -impl<'v> DfsOp for TestTarget<'v> { +impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { /// The element that was not found within R2. type Early = RegionElementIndex; @@ -204,12 +204,32 @@ impl<'v> DfsOp for TestTarget<'v> { fn add_universal_regions_outlived_by_source_to_target( &mut self, ) -> Result { - for ur in self.inferred_values + // For all `ur_in_source` in `source_region`. + for ur_in_source in self.inferred_values .universal_regions_outlived_by(self.source_region) { - if !self.inferred_values.contains(self.target_region, ur) { - return Err(self.elements.index(ur)); + // Check that `target_region` outlives `ur_in_source`. + + // If `ur_in_source` is a member of `target_region`, OK. + // + // (This is implied by the loop below, actually, just an + // irresistible micro-opt. Mm. Premature optimization. So + // tasty.) + if self.inferred_values.contains(self.target_region, ur_in_source) { + continue; } + + // If there is some other element X such that `target_region: X` and + // `X: ur_in_source`, OK. + if self.inferred_values + .universal_regions_outlived_by(self.target_region) + .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source)) + { + continue; + } + + // Otherwise, not known to be true. + return Err(self.elements.index(ur_in_source)); } Ok(false) 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 af88edc22cee3..86ba7524bf055 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -14,7 +14,7 @@ use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; -use rustc::infer::region_constraints::VarOrigins; +use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; @@ -24,7 +24,7 @@ use syntax_pos::Span; mod annotation; mod dfs; -use self::dfs::CopyFromSourceToTarget; +use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource}; mod dump_mir; mod graphviz; mod values; @@ -53,6 +53,9 @@ pub struct RegionInferenceContext<'tcx> { /// The constraints we have accumulated and used during solving. constraints: Vec, + /// Type constraints that we check after solving. + type_tests: Vec>, + /// Information about the universally quantified regions in scope /// on this function and their (known) relations to one another. universal_regions: UniversalRegions<'tcx>, @@ -95,6 +98,90 @@ pub struct Constraint { span: Span, } +/// A "type test" corresponds to an outlives constraint between a type +/// and a lifetime, like `T: 'x` or `::Bar: 'x`. They are +/// translated from the `Verify` region constraints in the ordinary +/// inference context. +/// +/// These sorts of constraints are handled differently than ordinary +/// constraints, at least at present. During type checking, the +/// `InferCtxt::process_registered_region_obligations` method will +/// attempt to convert a type test like `T: 'x` into an ordinary +/// outlives constraint when possible (for example, `&'a T: 'b` will +/// be converted into `'a: 'b` and registered as a `Constraint`). +/// +/// In some cases, however, there are outlives relationships that are +/// not converted into a region constraint, but rather into one of +/// these "type tests". The distinction is that a type test does not +/// influence the inference result, but instead just examines the +/// values that we ultimately inferred for each region variable and +/// checks that they meet certain extra criteria. If not, an error +/// can be issued. +/// +/// One reason for this is that these type tests always boil down to a +/// check like `'a: 'x` where `'a` is a universally quantified region +/// -- and therefore not one whose value is really meant to be +/// *inferred*, precisely. Another reason is that these type tests can +/// involve *disjunction* -- that is, they can be satisfied in more +/// than one way. +/// +/// For more information about this translation, see +/// `InferCtxt::process_registered_region_obligations` and +/// `InferCtxt::type_must_outlive` in `rustc::infer::outlives`. +#[derive(Clone, Debug)] +pub struct TypeTest<'tcx> { + /// The type `T` that must outlive the region. + pub generic_kind: GenericKind<'tcx>, + + /// The region `'x` that the type must outlive. + pub lower_bound: RegionVid, + + /// The point where the outlives relation must hold. + pub point: Location, + + /// Where did this constraint arise? + pub span: Span, + + /// A test which, if met by the region `'x`, proves that this type + /// constraint is satisfied. + pub test: RegionTest, +} + +/// A "test" that can be applied to some "subject region" `'x`. These are used to +/// describe type constraints. Tests do not presently affect the +/// region values that get inferred for each variable; they only +/// examine the results *after* inference. This means they can +/// conveniently include disjuction ("a or b must be true"). +#[derive(Clone, Debug)] +pub enum RegionTest { + /// The subject region `'x` must by outlived by *some* region in + /// the given set of regions. + /// + /// This test comes from e.g. a where clause like `T: 'a + 'b`, + /// which implies that we know that `T: 'a` and that `T: + /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can + /// do so by showing that `'a: 'x` *or* `'b: 'x`. + IsOutlivedByAnyRegionIn(Vec), + + /// The subject region `'x` must by outlived by *all* regions in + /// the given set of regions. + /// + /// This test comes from e.g. a projection type like `T = >::Foo`, which must outlive `'a` or `'b`, and + /// maybe both. Therefore we can prove that `T: 'x` if we know + /// that `'a: 'x` *and* `'b: 'x`. + IsOutlivedByAllRegionsIn(Vec), + + /// Any of the given tests are true. + /// + /// This arises from projections, for which there are multiple + /// ways to prove an outlives relationship. + Any(Vec), + + /// All of the given tests are true. + All(Vec), +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -122,6 +209,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { liveness_constraints: RegionValues::new(elements, num_region_variables), inferred_values: None, constraints: Vec::new(), + type_tests: Vec::new(), universal_regions, }; @@ -243,7 +331,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); } - /// Perform region inference. + /// Add a "type test" that must be satisfied. + pub(super) fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { + self.type_tests.push(type_test); + } + + /// Perform region inference and report errors if we see any + /// unsatisfiable constraints. If this is a closure, returns the + /// region requirements to propagate to our creator, if any. pub(super) fn solve( &mut self, infcx: &InferCtxt<'_, '_, 'tcx>, @@ -254,6 +349,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.propagate_constraints(mir); + self.check_type_tests(infcx, mir); + let outlives_requirements = self.check_universal_regions(infcx, mir_def_id); if outlives_requirements.is_empty() { @@ -315,6 +412,123 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } + /// Once regions have been propagated, this method is used to 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 } + /// + /// 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. + fn check_type_tests( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + ) { + for type_test in &self.type_tests { + debug!("check_type_test: {:?}", type_test); + + if !self.eval_region_test( + mir, + type_test.point, + type_test.lower_bound, + &type_test.test, + ) { + // Oh the humanity. Obviously we will do better than this error eventually. + infcx.tcx.sess.span_err( + type_test.span, + &format!("failed type test: {:?}", type_test), + ); + } + } + } + + /// Test if `test` is true when applied to `lower_bound` at + /// `point`, and returns true or false. + fn eval_region_test( + &self, + mir: &Mir<'tcx>, + point: Location, + lower_bound: RegionVid, + test: &RegionTest, + ) -> bool { + debug!( + "eval_region_test(point={:?}, lower_bound={:?}, test={:?})", + point, + lower_bound, + test + ); + + match test { + RegionTest::IsOutlivedByAllRegionsIn(regions) => regions + .iter() + .all(|&r| self.eval_outlives(mir, r, lower_bound, point)), + + RegionTest::IsOutlivedByAnyRegionIn(regions) => regions + .iter() + .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), + + RegionTest::Any(tests) => tests + .iter() + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), + + RegionTest::All(tests) => tests + .iter() + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), + } + } + + // Evaluate whether `sup_region: sub_region @ point`. + fn eval_outlives( + &self, + mir: &Mir<'tcx>, + sup_region: RegionVid, + sub_region: RegionVid, + point: Location, + ) -> bool { + debug!( + "eval_outlives({:?}: {:?} @ {:?})", + sup_region, + sub_region, + point + ); + + // Roughly speaking, do a DFS of all region elements reachable + // from `point` contained in `sub_region`. If any of those are + // *not* present in `sup_region`, the DFS will abort early and + // yield an `Err` result. + match self.dfs( + mir, + TestTargetOutlivesSource { + source_region: sub_region, + target_region: sup_region, + constraint_point: point, + elements: &self.elements, + universal_regions: &self.universal_regions, + inferred_values: self.inferred_values.as_ref().unwrap(), + }, + ) { + Ok(_) => { + debug!("eval_outlives: true"); + true + } + + Err(elem) => { + debug!( + "eval_outlives: false because `{:?}` is not present in `{:?}`", + self.elements.to_element(elem), + sup_region + ); + false + } + } + } + /// Once regions have been propagated, this method is used to see /// whether any of the constraints were too strong. In particular, /// we want to check for a case where a universally quantified 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 c98a94fa8bc10..73c40827c540f 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -11,11 +11,14 @@ use rustc::mir::Mir; use rustc::infer::region_constraints::Constraint; use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::region_constraints::{Verify, VerifyBound}; use rustc::ty; +use syntax::codemap::Span; +use transform::type_check::Locations; use transform::type_check::MirTypeckRegionConstraints; use transform::type_check::OutlivesSet; -use super::region_infer::RegionInferenceContext; +use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest}; /// When the MIR type-checker executes, it validates all the types in /// the MIR, and in the process generates a set of constraints that @@ -27,10 +30,7 @@ pub(super) fn generate<'tcx>( mir: &Mir<'tcx>, constraints: &MirTypeckRegionConstraints<'tcx>, ) { - SubtypeConstraintGenerator { - regioncx, - mir, - }.generate(constraints); + SubtypeConstraintGenerator { regioncx, mir }.generate(constraints); } struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { @@ -65,6 +65,8 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { givens, } = data; + let span = self.mir.source_info(locations.from_location).span; + for constraint in constraints.keys() { debug!("generate: constraint: {:?}", constraint); let (a_vid, b_vid) = match constraint { @@ -81,12 +83,15 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { // reverse direction, because `regioncx` talks about // "outlives" (`>=`) whereas the region constraints // talk about `<=`. - let span = self.mir.source_info(locations.from_location).span; self.regioncx .add_outlives(span, b_vid, a_vid, locations.at_location); } - assert!(verifys.is_empty(), "verifys not yet implemented"); + for verify in verifys { + let type_test = self.verify_to_type_test(verify, span, locations); + self.regioncx.add_type_test(type_test); + } + assert!( givens.is_empty(), "MIR type-checker does not use givens (thank goodness)" @@ -94,6 +99,55 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { } } + fn verify_to_type_test( + &self, + verify: &Verify<'tcx>, + span: Span, + locations: &Locations, + ) -> TypeTest<'tcx> { + let generic_kind = verify.kind; + + let lower_bound = self.to_region_vid(verify.region); + + let point = locations.at_location; + + let test = self.verify_bound_to_region_test(&verify.bound); + + TypeTest { + generic_kind, + lower_bound, + point, + span, + test, + } + } + + fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { + match verify_bound { + VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AnyBound(bounds) => RegionTest::Any( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + + VerifyBound::AllBounds(bounds) => RegionTest::All( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + } + } + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { // Every region that we see in the constraints came from the // MIR or from the parameter environment. If the former, it diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs new file mode 100644 index 0000000000000..c6547ae68fa1e --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -0,0 +1,51 @@ +// 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. + +// compile-flags:-Znll -Zborrowck=mir + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box) -> Box +where + T: Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr new file mode 100644 index 0000000000000..5b29ff8862109 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn.rs:22:5 + | +22 | x + | ^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn.rs:38:5 + | +38 | x + | ^ + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#2r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn([]) } + --> $DIR/ty-param-fn.rs:22:5 + | +22 | x + | ^ + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-fn.rs:38:5 + | +38 | x + | ^ + +error: aborting due to 2 previous errors + From fad3d1d7fd16fea674bdafe49649fb52e15ab118 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Dec 2017 16:57:43 -0500 Subject: [PATCH 05/25] dfs.rs: rustfmt --- .../borrow_check/nll/region_infer/dfs.rs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs index dcf5d979a4c0a..59860d61ab985 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -13,7 +13,8 @@ use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::region_infer::RegionInferenceContext; -use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValues, RegionValueElements}; +use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements, + RegionValues}; use rustc::mir::{Location, Mir}; use rustc::ty::RegionVid; use rustc_data_structures::fx::FxHashSet; @@ -112,9 +113,7 @@ pub(super) trait DfsOp { /// Adds all universal regions in the source region to the target region, returning /// true if something has changed. - fn add_universal_regions_outlived_by_source_to_target( - &mut self, - ) -> Result; + fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result; } /// Used during inference to enforce a `R1: R2 @ P` constraint. For @@ -143,16 +142,11 @@ impl<'v> DfsOp for CopyFromSourceToTarget<'v> { .contains(self.source_region, point_index) } - fn add_to_target_region( - &mut self, - point_index: RegionElementIndex, - ) -> Result { + fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result { Ok(self.inferred_values.add(self.target_region, point_index)) } - fn add_universal_regions_outlived_by_source_to_target( - &mut self, - ) -> Result { + fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result { Ok( self.inferred_values .add_universal_regions_outlived_by(self.source_region, self.target_region), @@ -215,16 +209,19 @@ impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { // (This is implied by the loop below, actually, just an // irresistible micro-opt. Mm. Premature optimization. So // tasty.) - if self.inferred_values.contains(self.target_region, ur_in_source) { + if self.inferred_values + .contains(self.target_region, ur_in_source) + { continue; } // If there is some other element X such that `target_region: X` and // `X: ur_in_source`, OK. if self.inferred_values - .universal_regions_outlived_by(self.target_region) - .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source)) - { + .universal_regions_outlived_by(self.target_region) + .any(|ur_in_target| { + self.universal_regions.outlives(ur_in_target, ur_in_source) + }) { continue; } From 1c57468840f708e52db9b1e59b536c4f4783e823 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 12:03:44 -0500 Subject: [PATCH 06/25] move `type_check` out of `transform` and into the `nll` module --- src/librustc_mir/borrow_check/nll/mod.rs | 8 ++++---- .../borrow_check/nll/subtype_constraint_generation.rs | 6 +++--- .../type_check.rs => borrow_check/nll/type_check/mod.rs} | 0 src/librustc_mir/transform/mod.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/librustc_mir/{transform/type_check.rs => borrow_check/nll/type_check/mod.rs} (100%) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 218142f7e2d42..ffbb3d319174a 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -16,7 +16,6 @@ use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; use std::io; use transform::MirSource; -use transform::type_check; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; use borrow_check::FlowAtLocation; use dataflow::MaybeInitializedLvals; @@ -27,14 +26,15 @@ use util::pretty::{self, ALIGN}; use self::mir_util::PassWhere; mod constraint_generation; +pub(crate) mod region_infer; +mod renumber; mod subtype_constraint_generation; +pub(crate) mod type_check; mod universal_regions; -use self::universal_regions::UniversalRegions; -pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; +use self::universal_regions::UniversalRegions; -mod renumber; /// Rewrites the regions in the MIR to use NLL variables, also /// scraping out the set of universal regions (e.g., region parameters) 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 73c40827c540f..e42302761bfa3 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -14,11 +14,11 @@ use rustc::infer::region_constraints::RegionConstraintData; use rustc::infer::region_constraints::{Verify, VerifyBound}; use rustc::ty; use syntax::codemap::Span; -use transform::type_check::Locations; -use transform::type_check::MirTypeckRegionConstraints; -use transform::type_check::OutlivesSet; use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest}; +use super::type_check::Locations; +use super::type_check::MirTypeckRegionConstraints; +use super::type_check::OutlivesSet; /// When the MIR type-checker executes, it validates all the types in /// the MIR, and in the process generates a set of constraints that diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs similarity index 100% rename from src/librustc_mir/transform/type_check.rs rename to src/librustc_mir/borrow_check/nll/type_check/mod.rs diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index fb9daf07c71dc..563405fccc970 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use borrow_check::nll::type_check; use build; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Mir, Promoted}; @@ -30,7 +31,6 @@ pub mod simplify_branches; pub mod simplify; pub mod erase_regions; pub mod no_landing_pads; -pub mod type_check; pub mod rustc_peek; pub mod elaborate_drops; pub mod add_call_guards; From ebd086b67fe19d9fd861ec26eddf3eee2f734378 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 12:17:20 -0500 Subject: [PATCH 07/25] move `LivenessResults` from `nll` into `liveness` analysis --- src/librustc_mir/borrow_check/nll/mod.rs | 25 ++---------------- src/librustc_mir/util/liveness.rs | 33 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index ffbb3d319174a..725694fd5b74b 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -16,7 +16,7 @@ use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; use std::io; use transform::MirSource; -use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; +use util::liveness::{LivenessResults, LocalSet}; use borrow_check::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; @@ -86,23 +86,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets); // Compute what is live where. - let liveness = &LivenessResults { - regular: liveness::liveness_of_locals( - &mir, - LivenessMode { - include_regular_use: true, - include_drops: false, - }, - ), - - drop: liveness::liveness_of_locals( - &mir, - LivenessMode { - include_regular_use: false, - include_drops: true, - }, - ), - }; + let liveness = &LivenessResults::compute(mir); // Generate non-subtyping constraints. constraint_generation::generate_constraints( @@ -136,11 +120,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( (regioncx, closure_region_requirements) } -struct LivenessResults { - regular: LivenessResult, - drop: LivenessResult, -} - fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, liveness: &LivenessResults, diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 45c3fcd8a615d..5163f74dd2529 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -80,6 +80,39 @@ pub struct LivenessMode { pub include_drops: bool, } +/// A combination of liveness results, used in NLL. +pub struct LivenessResults { + /// Liveness results where a regular use makes a variable X live, + /// but not a drop. + pub regular: LivenessResult, + + /// Liveness results where a drop makes a variable X live, + /// but not a regular use. + pub drop: LivenessResult, +} + +impl LivenessResults { + pub fn compute<'tcx>(mir: &Mir<'tcx>) -> LivenessResults { + LivenessResults { + regular: liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: true, + include_drops: false, + }, + ), + + drop: liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: false, + include_drops: true, + }, + ), + } + } +} + /// Compute which local variables are live within the given function /// `mir`. The liveness mode `mode` determines what sorts of uses are /// considered to make a variable live (e.g., do drops count?). From 4a940b321534a1024078cce92fc69030bfd6c60d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 12:49:08 -0500 Subject: [PATCH 08/25] move `flow_in_progress` into `dataflow` and document it --- src/librustc_mir/borrow_check/mod.rs | 4 +- .../borrow_check/nll/constraint_generation.rs | 2 +- src/librustc_mir/borrow_check/nll/mod.rs | 2 +- src/librustc_mir/dataflow/at_location.rs | 42 +++++++++++++++---- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 39bcd2b6ae063..576d598579876 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -22,16 +22,16 @@ use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; use rustc::mir::ClosureRegionRequirements; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_set::{IdxSetBuf}; +use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; use syntax::ast; use syntax_pos::Span; use dataflow::{do_dataflow, DebugFormatted}; +use dataflow::FlowAtLocation; use dataflow::MoveDataParamEnv; use dataflow::{DataflowAnalysis, DataflowResultsConsumer}; -use dataflow::{FlowAtLocation, FlowsAtLocation}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{EverInitializedLvals, MovingOutStatements}; use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex}; diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 59e862a56af02..5564a0d98afa3 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -22,7 +22,7 @@ use rustc::ty::fold::TypeFoldable; use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; -use borrow_check::{FlowAtLocation, FlowsAtLocation}; +use dataflow::{FlowAtLocation, FlowsAtLocation}; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::{HasMoveData, MoveData}; diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 725694fd5b74b..c616f027331b7 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -17,7 +17,7 @@ use std::collections::BTreeSet; use std::io; use transform::MirSource; use util::liveness::{LivenessResults, LocalSet}; -use borrow_check::FlowAtLocation; +use dataflow::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs index a6c3398489ad7..7f243ad6e264c 100644 --- a/src/librustc_mir/dataflow/at_location.rs +++ b/src/librustc_mir/dataflow/at_location.rs @@ -25,26 +25,43 @@ use std::iter; /// There's probably a way to auto-impl this, but I think /// it is cleaner to have manual visitor impls. pub trait FlowsAtLocation { - // reset the state bitvector to represent the entry to block `bb`. + /// Reset the state bitvector to represent the entry to block `bb`. fn reset_to_entry_of(&mut self, bb: BasicBlock); - // build gen + kill sets for statement at `loc`. + /// Build gen + kill sets for statement at `loc`. + /// + /// Note that invoking this method alone does not change the + /// `curr_state` -- you must invoke `apply_local_effect` + /// afterwards. fn reconstruct_statement_effect(&mut self, loc: Location); - // build gen + kill sets for terminator for `loc`. + /// Build gen + kill sets for terminator for `loc`. + /// + /// Note that invoking this method alone does not change the + /// `curr_state` -- you must invoke `apply_local_effect` + /// afterwards. fn reconstruct_terminator_effect(&mut self, loc: Location); - // apply current gen + kill sets to `flow_state`. - // - // (`bb` and `stmt_idx` parameters can be ignored if desired by - // client. For the terminator, the `stmt_idx` will be the number - // of statements in the block.) + /// Apply current gen + kill sets to `flow_state`. + /// + /// (`loc` parameters can be ignored if desired by + /// client. For the terminator, the `stmt_idx` will be the number + /// of statements in the block.) fn apply_local_effect(&mut self, loc: Location); } /// Represents the state of dataflow at a particular /// CFG location, both before and after it is /// executed. +/// +/// Data flow results are typically computed only as basic block +/// boundaries. A `FlowInProgress` allows you to reconstruct the +/// effects at any point in the control-flow graph by starting with +/// the state at the start of the basic block (`reset_to_entry_of`) +/// and then replaying the effects of statements and terminators +/// (e.g. via `reconstruct_statement_effect` and +/// `reconstruct_terminator_effect`; don't forget to call +/// `apply_local_effect`). pub struct FlowAtLocation where BD: BitDenotation, @@ -59,6 +76,7 @@ impl FlowAtLocation where BD: BitDenotation, { + /// Iterate over each bit set in the current state. pub fn each_state_bit(&self, f: F) where F: FnMut(BD::Idx), @@ -67,6 +85,9 @@ where .each_bit(self.base_results.operator().bits_per_block(), f) } + /// Iterate over each `gen` bit in the current effect (invoke + /// `reconstruct_statement_effect` or + /// `reconstruct_terminator_effect` first). pub fn each_gen_bit(&self, f: F) where F: FnMut(BD::Idx), @@ -88,6 +109,7 @@ where } } + /// Access the underlying operator. pub fn operator(&self) -> &BD { self.base_results.operator() } @@ -96,11 +118,15 @@ where self.curr_state.contains(x) } + /// Returns an iterator over the elements present in the current state. pub fn elems_incoming(&self) -> iter::Peekable> { let univ = self.base_results.sets().bits_per_block(); self.curr_state.elems(univ).peekable() } + /// Creates a clone of the current state and applies the local + /// effects to the clone (leaving the state of self intact). + /// Invokes `f` with an iterator over the resulting state. pub fn with_elems_outgoing(&self, f: F) where F: FnOnce(indexed_set::Elems), From 47c1921b9adb7c6e783ef4ec2a5f1dbe312b3da7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 13:14:36 -0500 Subject: [PATCH 09/25] move some parts of liveness to happen during type checking This allows us to re-use the `normalize` method on `TypeCheck`, which is important since normalization may create fresh region variables. This is not an ideal solution, though, since the current representation of "liveness constraints" (a vector of (region, point) pairs) is rather inefficient. Could do somewhat better by converting to indices, but it'd still be less good than the older code. Unclear how important this is. --- .../borrow_check/nll/constraint_generation.rs | 173 +------------- src/librustc_mir/borrow_check/nll/mod.rs | 23 +- .../borrow_check/nll/region_infer/mod.rs | 18 +- .../borrow_check/nll/type_check/liveness.rs | 220 ++++++++++++++++++ .../borrow_check/nll/type_check/mod.rs | 37 ++- src/test/ui/nll/ty-outlives/projection-fn.rs | 53 +++++ .../ui/nll/ty-outlives/projection-fn.stderr | 26 +++ 7 files changed, 351 insertions(+), 199 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/liveness.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-fn.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-fn.stderr diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 5564a0d98afa3..673e85e6b61cd 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -12,21 +12,13 @@ use rustc::hir; use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Place::Projection; -use rustc::mir::{Local, PlaceProjection, ProjectionElem}; +use rustc::mir::{PlaceProjection, ProjectionElem}; use rustc::mir::visit::TyContext; use rustc::infer::InferCtxt; -use rustc::traits::{self, ObligationCause}; -use rustc::ty::{self, ClosureSubsts, Ty}; +use rustc::ty::{self, ClosureSubsts}; 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 dataflow::{FlowAtLocation, FlowsAtLocation}; -use dataflow::MaybeInitializedLvals; -use dataflow::move_paths::{HasMoveData, MoveData}; -use super::LivenessResults; use super::ToRegionVid; use super::region_infer::RegionInferenceContext; @@ -34,19 +26,11 @@ 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 FlowAtLocation>, - move_data: &MoveData<'tcx>, ) { let mut cg = ConstraintGeneration { infcx, regioncx, mir, - liveness, - param_env, - flow_inits, - move_data, }; for (bb, data) in mir.basic_blocks().iter_enumerated() { @@ -59,16 +43,10 @@ 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 FlowAtLocation>, - move_data: &'cg MoveData<'tcx>, } - 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); } @@ -130,84 +108,6 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx } 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, bb: BasicBlock) { - 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); - } - }); - - 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 - .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(location); - } - } - /// Some variable with type `live_ty` is "regular live" at /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at @@ -230,75 +130,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { }); } - /// Some variable with type `live_ty` is "drop live" at `location` - /// -- i.e., it may be dropped later. This means that *some* of - /// the regions in its type must be live at `location`. The - /// precise set will depend on the dropck constraints, and in - /// particular this takes `#[may_dangle]` into account. - fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { - debug!( - "add_drop_live_constraint(dropped_ty={:?}, location={:?})", - dropped_ty, - location - ); - - let tcx = self.infcx.tcx; - let mut types = vec![(dropped_ty, 0)]; - let mut known = FxHashSet(); - while let Some((ty, depth)) = types.pop() { - let span = DUMMY_SP; // FIXME - let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { - Ok(result) => result, - Err(ErrorReported) => { - continue; - } - }; - - let ty::DtorckConstraint { - outlives, - dtorck_types, - } = result; - - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - for outlive in outlives { - self.add_regular_live_constraint(outlive, location); - } - - // However, there may also be some types that - // `dtorck_constraint_for_ty` could not resolve (e.g., - // associated types and parameters). We need to normalize - // associated types here and possibly recursively process. - for ty in dtorck_types { - let cause = ObligationCause::dummy(); - // We know that our original `dropped_ty` is well-formed, - // so region obligations resulting from this normalization - // should always hold. - // - // Therefore we ignore them instead of trying to match - // them up with a location. - let fulfillcx = traits::FulfillmentContext::new_ignoring_regions(); - match traits::fully_normalize_with_fulfillcx( - self.infcx, fulfillcx, cause, self.param_env, &ty - ) { - Ok(ty) => match ty.sty { - ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { - self.add_regular_live_constraint(ty, location); - } - - _ => if known.insert(ty) { - types.push((ty, depth + 1)); - }, - }, - - Err(errors) => { - self.infcx.report_fulfillment_errors(&errors, None); - } - } - } - } - } - fn add_reborrow_constraint( &mut self, location: Location, diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index c616f027331b7..3329448e2b7ac 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -77,7 +77,16 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, '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); + let liveness = &LivenessResults::compute(mir); + let constraint_sets = &type_check::type_check( + infcx, + mir_node_id, + param_env, + mir, + &liveness, + flow_inits, + move_data, + ); // Create the region inference context, taking ownership of the region inference // data that was contained in `infcx`. @@ -85,19 +94,9 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir); subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets); - // Compute what is live where. - let liveness = &LivenessResults::compute(mir); // Generate non-subtyping constraints. - constraint_generation::generate_constraints( - infcx, - &mut regioncx, - &mir, - param_env, - liveness, - flow_inits, - move_data, - ); + constraint_generation::generate_constraints(infcx, &mut regioncx, &mir); // Solve the region constraints. let closure_region_requirements = regioncx.solve(infcx, &mir, def_id); 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 86ba7524bf055..f36392c9fcfdb 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -425,11 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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. - fn check_type_tests( - &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - mir: &Mir<'tcx>, - ) { + fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -473,13 +469,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { .iter() .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), - RegionTest::Any(tests) => tests - .iter() - .any(|test| self.eval_region_test(mir, point, lower_bound, test)), + RegionTest::Any(tests) => tests.iter().any(|test| { + self.eval_region_test(mir, point, lower_bound, test) + }), - RegionTest::All(tests) => tests - .iter() - .all(|test| self.eval_region_test(mir, point, lower_bound, test)), + RegionTest::All(tests) => tests.iter().all(|test| { + self.eval_region_test(mir, point, lower_bound, test) + }), } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs new file mode 100644 index 0000000000000..e41bf7cda8e6c --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -0,0 +1,220 @@ +// 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. + +use dataflow::{FlowAtLocation, FlowsAtLocation}; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::{HasMoveData, MoveData}; +use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::mir::Local; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::util::common::ErrorReported; +use rustc_data_structures::fx::FxHashSet; +use syntax::codemap::DUMMY_SP; +use util::liveness::LivenessResults; + +use super::TypeChecker; + +/// Combines liveness analysis with initialization analysis to +/// determine which variables are live at which points, both due to +/// ordinary uses and drops. Returns a set of (ty, location) pairs +/// that indicate which types must be live at which point in the CFG. +/// This vector is consumed by `constraint_generation`. +/// +/// NB. This computation requires normalization; therefore, it must be +/// performed before +pub(super) fn generate<'gcx, 'tcx>( + cx: &mut TypeChecker<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation>, + move_data: &MoveData<'tcx>, +) { + let tcx = cx.tcx(); + let mut generator = TypeLivenessGenerator { + cx, + tcx, + mir, + liveness, + flow_inits, + move_data, + }; + + for bb in mir.basic_blocks().indices() { + generator.add_liveness_constraints(bb); + } +} + +struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> +where + 'typeck: 'gen, + 'flow: 'gen, + 'tcx: 'typeck + 'flow, + 'gcx: 'tcx, +{ + cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, + tcx: TyCtxt<'typeck, 'gcx, 'tcx>, + mir: &'gen Mir<'tcx>, + liveness: &'gen LivenessResults, + flow_inits: &'gen mut FlowAtLocation>, + move_data: &'gen MoveData<'tcx>, +} + +impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, '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, bb: BasicBlock) { + 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.push_type_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.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(location); + } + } + + /// Some variable with type `live_ty` is "regular live" at + /// `location` -- i.e., it may be used later. This means that all + /// regions appearing in the type `live_ty` must be live at + /// `location`. + fn push_type_live_constraint(&mut self, value: T, location: Location) + where + T: TypeFoldable<'tcx>, + { + debug!( + "push_type_live_constraint(live_ty={:?}, location={:?})", + value, + location + ); + + self.tcx.for_each_free_region(&value, |live_region| { + self.cx + .constraints + .liveness_set + .push((live_region, location)); + }); + } + + /// Some variable with type `live_ty` is "drop live" at `location` + /// -- i.e., it may be dropped later. This means that *some* of + /// the regions in its type must be live at `location`. The + /// precise set will depend on the dropck constraints, and in + /// particular this takes `#[may_dangle]` into account. + fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { + debug!( + "add_drop_live_constraint(dropped_ty={:?}, location={:?})", + dropped_ty, + location + ); + + let tcx = self.cx.infcx.tcx; + let mut types = vec![(dropped_ty, 0)]; + let mut known = FxHashSet(); + while let Some((ty, depth)) = types.pop() { + let span = DUMMY_SP; // FIXME + let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { + Ok(result) => result, + Err(ErrorReported) => { + continue; + } + }; + + let ty::DtorckConstraint { + outlives, + dtorck_types, + } = result; + + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. + for outlive in outlives { + self.push_type_live_constraint(outlive, location); + } + + // However, there may also be some types that + // `dtorck_constraint_for_ty` could not resolve (e.g., + // associated types and parameters). We need to normalize + // associated types here and possibly recursively process. + for ty in dtorck_types { + let ty = self.cx.normalize(&ty, location); + match ty.sty { + ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { + self.push_type_live_constraint(ty, location); + } + + _ => if known.insert(ty) { + types.push((ty, depth + 1)); + }, + } + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index fae911780a1e6..8f18105600aed 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -12,6 +12,9 @@ #![allow(unreachable_code)] use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; +use dataflow::FlowAtLocation; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::MoveData; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::region_constraints::RegionConstraintData; use rustc::traits::{self, FulfillmentContext}; @@ -26,22 +29,43 @@ use std::fmt; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; +use util::liveness::LivenessResults; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +mod liveness; + /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to -/// be proven. +/// be proven. This result is includes liveness constraints that +/// ensure that regions appearing in the types of all local variables +/// are live at all points where that local variable may later be +/// used. /// /// This phase of type-check ought to be infallible -- this is because /// the original, HIR-based type-check succeeded. So if any errors /// occur here, we will get a `bug!` reported. -pub fn type_check<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +pub(crate) fn type_check<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation>, + move_data: &MoveData<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + type_check_internal(infcx, body_id, param_env, mir, &mut |cx| { + liveness::generate(cx, mir, liveness, flow_inits, move_data) + }) +} + +fn type_check_internal<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { let mut checker = TypeChecker::new(infcx, body_id, param_env); let errors_reported = { @@ -55,9 +79,12 @@ pub fn type_check<'a, 'gcx, 'tcx>( checker.typeck_mir(mir); } + extra(&mut checker); + checker.constraints } + fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { // We sometimes see MIR failures (notably predicate failures) due to // the fact that we check rvalue sized predicates here. So use `delay_span_bug` @@ -503,7 +530,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { /// constraints needed for it to be valid and well-typed. Along the /// way, it accrues region constraints -- these can later be used by /// NLL region checking. -pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { +struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, last_span: Span, @@ -1474,7 +1501,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _region_constraint_sets = type_check(&infcx, id, param_env, mir); + let _ = type_check_internal(&infcx, id, param_env, mir, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/test/ui/nll/ty-outlives/projection-fn.rs b/src/test/ui/nll/ty-outlives/projection-fn.rs new file mode 100644 index 0000000000000..677d13527884a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-fn.rs @@ -0,0 +1,53 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir + +#![allow(warnings)] +#![feature(dyn_trait)] + +trait Anything { } + +impl Anything for T { } + +fn no_region<'a, T>(mut x: T) -> Box +where + T: Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(mut x: T) -> Box +where + T: 'a + Iterator, +{ + Box::new(x.next()) +} + +fn wrong_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + Box::new(x.next()) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr new file mode 100644 index 0000000000000..a478348edc11f --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) } + --> $DIR/projection-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 02e65250e4dfb22d5e1bc1e637913cbcfc60db13 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 04:47:59 -0500 Subject: [PATCH 10/25] mild refactors of the control flow (no functional changes) In the future, `check_type_tests` will also potentially propagate constriants to its caller. --- .../borrow_check/nll/region_infer/mod.rs | 72 +++++++++++-------- 1 file changed, 44 insertions(+), 28 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 f36392c9fcfdb..efa0734b01684 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -349,9 +349,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.propagate_constraints(mir); + // If this is a closure, we can propagate unsatisfied + // `outlives_requirements` to our creator, so create a vector + // to store those. Otherwise, we'll pass in `None` to the + // functions below, which will trigger them to report errors + // eagerly. + let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) { + Some(vec![]) + } else { + None + }; + self.check_type_tests(infcx, mir); - let outlives_requirements = self.check_universal_regions(infcx, mir_def_id); + self.check_universal_regions(infcx, outlives_requirements.as_mut()); + + let outlives_requirements = outlives_requirements.unwrap_or(vec![]); if outlives_requirements.is_empty() { None @@ -429,18 +442,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if !self.eval_region_test( + if self.eval_region_test( mir, type_test.point, type_test.lower_bound, &type_test.test, ) { - // Oh the humanity. Obviously we will do better than this error eventually. - infcx.tcx.sess.span_err( - type_test.span, - &format!("failed type test: {:?}", type_test), - ); + continue; } + + // Oh the humanity. Obviously we will do better than this error eventually. + infcx.tcx.sess.span_err( + type_test.span, + &format!("failed type test: {:?}", type_test), + ); } } @@ -538,11 +553,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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. + /// + /// If `propagated_outlives_requirements` is `Some`, then we will + /// push unsatisfied obligations into there. Otherwise, we'll + /// report them as errors. fn check_universal_regions( &self, infcx: &InferCtxt<'_, '_, 'tcx>, - mir_def_id: DefId, - ) -> Vec { + mut propagated_outlives_requirements: Option<&mut Vec>, + ) { // The universal regions are always found in a prefix of the // full list. let universal_definitions = self.definitions @@ -555,25 +574,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut outlives_requirements = vec![]; for (fr, _) in universal_definitions { self.check_universal_region(infcx, fr, &mut outlives_requirements); - } - - // If this is a closure, we can propagate unsatisfied - // `outlives_requirements` to our creator. Otherwise, we have - // to report a hard error here. - if infcx.tcx.is_closure(mir_def_id) { - return outlives_requirements; - } - for outlives_requirement in outlives_requirements { - self.report_error( - infcx, - outlives_requirement.free_region, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); + // Propagate unsatisfied requirements if possible, else + // report them. + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { + propagated_outlives_requirements.extend(outlives_requirements.drain(..)); + } else { + for outlives_requirement in outlives_requirements.drain(..) { + self.report_error( + infcx, + outlives_requirement.free_region, + outlives_requirement.outlived_free_region, + outlives_requirement.blame_span, + ); + } + } } - - vec![] } /// Check the final value for the free region `fr` to see if it @@ -588,7 +604,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, '_, 'tcx>, longer_fr: RegionVid, - outlives_requirements: &mut Vec, + propagated_outlives_requirements: &mut Vec, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -626,7 +642,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); // Push the constraint `fr-: shorter_fr+` - outlives_requirements.push(ClosureOutlivesRequirement { + propagated_outlives_requirements.push(ClosureOutlivesRequirement { free_region: fr_minus, outlived_free_region: shorter_fr_plus, blame_span: blame_span, From 154cd9419763f35c0f38b1cb044f43eeece1ebf0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 05:11:36 -0500 Subject: [PATCH 11/25] rework region flags: 'static can be erased too The existing flags did not consider `'static` to be "free". This then fed into what was "erasable" -- but `'static` is most certainly erasable. --- src/librustc/ty/fold.rs | 15 ++++++++++----- src/librustc/ty/mod.rs | 10 ++++++++++ src/librustc/ty/sty.rs | 17 ++++++++++++++--- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 069dc0275cbb1..c5b82730e488c 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -97,14 +97,19 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn has_closure_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_CLOSURE) } + /// "Free" regions in this context means that it has any region + /// that is not (a) erased or (b) late-bound. + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + /// True if there any any un-erased free regions. fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | - TypeFlags::HAS_FREE_REGIONS) + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) } + fn is_normalized_for_trans(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | + !self.has_type_flags(TypeFlags::HAS_RE_INFER | TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_TY_INFER | TypeFlags::HAS_PARAMS | diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4e1a79d4613e7..9d088b95bbbb5 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -421,8 +421,18 @@ bitflags! { const HAS_TY_INFER = 1 << 2; const HAS_RE_INFER = 1 << 3; const HAS_RE_SKOL = 1 << 4; + + /// Does this have any `ReEarlyBound` regions? Used to + /// determine whether substitition is required, since those + /// represent regions that are bound in a `ty::Generics` and + /// hence may be substituted. const HAS_RE_EARLY_BOUND = 1 << 5; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but `ReLateBound` and `ReErased`. const HAS_FREE_REGIONS = 1 << 6; + + /// Is an error type reachable? const HAS_TY_ERR = 1 << 7; const HAS_PROJECTION = 1 << 8; diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index e085d1311c3ff..2f72b3dde42f1 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1184,18 +1184,29 @@ impl RegionKind { match *self { ty::ReVar(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; } ty::ReSkolemized(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; flags = flags | TypeFlags::HAS_RE_SKOL; flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; } ty::ReLateBound(..) => { } - ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; } - ty::ReStatic | ty::ReErased => { } - _ => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } + ty::ReEarlyBound(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; + } + ty::ReEmpty | + ty::ReStatic | + ty::ReFree { .. } | + ty::ReScope { .. } => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } + ty::ReErased => { + } } match *self { From c7cfa2367b0ad4aed16dff7ad3d8d3e9eea6540a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 05:40:43 -0500 Subject: [PATCH 12/25] thread through an implicit region body of the fn body --- src/librustc_mir/borrow_check/nll/mod.rs | 2 + .../borrow_check/nll/region_infer/mod.rs | 4 +- .../borrow_check/nll/type_check/mod.rs | 28 +++++++++---- .../borrow_check/nll/universal_regions.rs | 15 ++++++- src/test/mir-opt/nll/named-lifetimes-basic.rs | 10 +++-- src/test/mir-opt/nll/reborrow-basic.rs | 8 ++-- src/test/mir-opt/nll/region-liveness-basic.rs | 8 ++-- .../nll/region-liveness-drop-may-dangle.rs | 4 +- .../nll/region-liveness-drop-no-may-dangle.rs | 4 +- .../nll/region-liveness-two-disjoint-uses.rs | 12 +++--- .../mir-opt/nll/region-subtyping-basic.rs | 12 +++--- src/test/ui/nll/capture-ref-in-struct.stderr | 2 +- .../escape-argument.stderr | 2 +- .../escape-upvar-nested.stderr | 2 +- .../escape-upvar-ref.stderr | 2 +- ...er-to-static-comparing-against-free.stderr | 2 +- .../ui/nll/ty-outlives/projection-fn.stderr | 4 +- .../ui/nll/ty-outlives/ty-param-fn-body.rs | 41 +++++++++++++++++++ .../nll/ty-outlives/ty-param-fn-body.stderr | 14 +++++++ .../ui/nll/ty-outlives/ty-param-fn.stderr | 4 +- 20 files changed, 135 insertions(+), 45 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn-body.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 3329448e2b7ac..9e7c94cd7ffb7 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -78,11 +78,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // Run the MIR type-checker. let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); let liveness = &LivenessResults::compute(mir); + let fr_fn_body = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let constraint_sets = &type_check::type_check( infcx, mir_node_id, param_env, mir, + fr_fn_body, &liveness, flow_inits, move_data, 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 efa0734b01684..4759185d22623 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -439,6 +439,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// have no evidence that `'b` outlives `'a`, so we want to report /// an error. fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { + let tcx = infcx.tcx; + for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -452,7 +454,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Oh the humanity. Obviously we will do better than this error eventually. - infcx.tcx.sess.span_err( + tcx.sess.span_err( type_test.span, &format!("failed type test: {:?}", type_test), ); diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 8f18105600aed..f7493f653e081 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -51,13 +51,19 @@ pub(crate) fn type_check<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + implicit_region_bound: ty::Region<'tcx>, liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) -> MirTypeckRegionConstraints<'tcx> { - type_check_internal(infcx, body_id, param_env, mir, &mut |cx| { - liveness::generate(cx, mir, liveness, flow_inits, move_data) - }) + type_check_internal( + infcx, + body_id, + param_env, + mir, + Some(implicit_region_bound), + &mut |cx| liveness::generate(cx, mir, liveness, flow_inits, move_data), + ) } fn type_check_internal<'gcx, 'tcx>( @@ -65,9 +71,10 @@ fn type_check_internal<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + implicit_region_bound: Option>, extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { - let mut checker = TypeChecker::new(infcx, body_id, param_env); + let mut checker = TypeChecker::new(infcx, body_id, param_env, implicit_region_bound); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); verifier.visit_mir(mir); @@ -535,6 +542,7 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'gcx>, last_span: Span, body_id: ast::NodeId, + implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, constraints: MirTypeckRegionConstraints<'tcx>, } @@ -588,12 +596,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, + implicit_region_bound: Option>, ) -> Self { TypeChecker { infcx, last_span: DUMMY_SP, body_id, param_env, + implicit_region_bound, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), } @@ -618,8 +628,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { span_mirbug!(self, "", "errors selecting obligation: {:?}", e); } - self.infcx - .process_registered_region_obligations(&[], None, self.param_env, self.body_id); + self.infcx.process_registered_region_obligations( + &[], + self.implicit_region_bound, + self.param_env, + self.body_id, + ); let data = self.infcx.take_and_reset_region_constraints(); if !data.is_empty() { @@ -1501,7 +1515,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _ = type_check_internal(&infcx, id, param_env, mir, &mut |_| ()); + let _ = type_check_internal(&infcx, id, param_env, mir, None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5336bd271f590..ce931a68a0c3b 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -45,6 +45,11 @@ pub struct UniversalRegions<'tcx> { /// The vid assigned to `'static` pub fr_static: RegionVid, + /// A special region vid created to represent the current MIR fn + /// body. It will outlive the entire CFG but it will not outlive + /// any other universal regions. + pub fr_fn_body: RegionVid, + /// We create region variables such that they are ordered by their /// `RegionClassification`. The first block are globals, then /// externals, then locals. So things from: @@ -408,6 +413,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { let first_local_index = self.infcx.num_region_vars(); let inputs_and_output = self.infcx .replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output); + let fr_fn_body = self.infcx.next_nll_region_var(FR).to_region_vid(); let num_universals = self.infcx.num_region_vars(); // Insert the facts we know from the predicates. Why? Why not. @@ -419,12 +425,16 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { self.add_implied_bounds(&indices, ty); } - // Finally, outlives is reflexive, and static outlives every - // other free region. + // Finally: + // - outlives is reflexive, so `'r: 'r` for every region `'r` + // - `'static: 'r` for every region `'r` + // - `'r: 'fn_body` for every (other) universally quantified + // region `'r`, all of which are provided by our caller for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) { debug!("build: relating free region {:?} to itself and to 'static", fr); self.relations.relate_universal_regions(fr, fr); self.relations.relate_universal_regions(fr_static, fr); + self.relations.relate_universal_regions(fr, fr_fn_body); } let (output_ty, input_tys) = inputs_and_output.split_last().unwrap(); @@ -445,6 +455,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { UniversalRegions { indices, fr_static, + fr_fn_body, first_extern_index, first_local_index, num_universals, diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index f3a57c088409a..71304f71b6101 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -27,16 +27,18 @@ fn main() { // END RUST SOURCE // START rustc.use_x.nll.0.mir // | Free Region Mapping -// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#3r] -// | '_#1r | External | ['_#1r] -// | '_#2r | External | ['_#2r, '_#1r] -// | '_#3r | Local | ['_#3r] +// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] +// | '_#1r | External | ['_#1r, '_#4r] +// | '_#2r | External | ['_#2r, '_#1r, '_#4r] +// | '_#3r | Local | ['_#4r, '_#3r] +// | '_#4r | Local | ['_#4r] // | // | Inferred Region Values // | '_#0r | {'_#0r, bb0[0], bb0[1]} // | '_#1r | {'_#1r, bb0[0], bb0[1]} // | '_#2r | {'_#2r, bb0[0], bb0[1]} // | '_#3r | {'_#3r, bb0[0], bb0[1]} +// | '_#4r | {'_#4r, bb0[0], bb0[1]} // | // ... // fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index d203472f20c73..9e6d6aaee15be 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,11 +28,11 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#6r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#7r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// | '_#8r | {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#9r | {bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// let _2: &'_#6r mut i32; +// let _2: &'_#7r mut i32; // ... -// let _4: &'_#8r mut i32; +// let _4: &'_#9r mut i32; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index c04cedbc04b4d..ab99d79d7f388 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,15 +31,15 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]} -// | '_#2r | {bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#3r | {bb2[1], bb3[0], bb3[1]} // ... -// let _2: &'_#2r usize; +// let _2: &'_#3r usize; // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // bb2: { // | Live variables on entry to bb2[0]: [_1, _3] -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // | Live variables on entry to bb2[1]: [_2] // switchInt(const true) -> [0u8: bb4, otherwise: bb3]; // } diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index e2ad49a443625..515772a942711 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,5 +44,7 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#5r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// ... +// let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index e0272a51d03d9..a257910b0b80b 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,5 +46,7 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#5r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// ... +// let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir 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 8aa0eb1a3a90e..3041c2cb06162 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,14 +36,14 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} // ... -// | '_#3r | {bb8[1], bb8[2], bb8[3], bb8[4]} -// | '_#4r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} +// | '_#4r | {bb8[1], bb8[2], bb8[3], bb8[4]} +// | '_#5r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} // ... -// let mut _2: &'_#4r usize; +// let mut _2: &'_#5r usize; // ... -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // ... -// _2 = &'_#3r (*_10); +// _2 = &'_#4r (*_10); // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 2bc165bd3c4a3..5a3f831331c43 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,16 +32,16 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#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]} +// | '_#2r | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#3r | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#4r | {bb2[5], bb2[6], bb3[0], bb3[1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#2r usize; +// let _2: &'_#3r usize; // ... -// let _6: &'_#3r usize; +// let _6: &'_#4r usize; // ... -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // ... // _7 = _2; // ... diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr index 6b57f91987be5..9f62b2a41c5b9 100644 --- a/src/test/ui/nll/capture-ref-in-struct.stderr +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -7,7 +7,7 @@ error[E0597]: `y` does not live long enough 37 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#4r... + = note: borrowed value must be valid for lifetime '_#5r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 567ed299ac150..2b0e3661376d3 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -32,7 +32,7 @@ error[E0597]: `y` does not live long enough 39 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#5r... + = note: borrowed value must be valid for lifetime '_#6r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 6c70afa0c9c8a..ddda72c5686ef 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -59,7 +59,7 @@ error[E0597]: `y` does not live long enough 36 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#3r... + = note: borrowed value must be valid for lifetime '_#4r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 0b982dd812b59..a0814cfc15fa4 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -36,7 +36,7 @@ error[E0597]: `y` does not live long enough 36 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#3r... + = note: borrowed value must be valid for lifetime '_#4r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 3e54e62d0110f..c5107322f6f2c 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -79,7 +79,7 @@ error[E0597]: `a` does not live long enough 49 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#1r... + = note: borrowed value must be valid for lifetime '_#2r... error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr index a478348edc11f..77eabef654566 100644 --- a/src/test/ui/nll/ty-outlives/projection-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) } +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } --> $DIR/projection-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } --> $DIR/projection-fn.rs:40:5 | 40 | Box::new(x.next()) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs new file mode 100644 index 0000000000000..42e62b2389685 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -0,0 +1,41 @@ +// 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. + +// compile-flags:-Znll -Zborrowck=mir + +// Test that we assume that universal types like `T` outlive the +// function body. + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::cell::Cell; + +// No errors here, because `'a` is local to the body. +fn region_within_body(t: T) { + let some_int = 22; + let cell = Cell::new(&some_int); + outlives(cell, t) +} + +// Error here, because T: 'a is not satisfied. +fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { + outlives(cell, t) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives<'a, T>(x: Cell<&'a usize>, y: T) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr new file mode 100644 index 0000000000000..3cc335bb29db2 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn-body.rs:30:5 + | +30 | outlives(cell, t) + | ^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#4r, point: bb0[4], span: $DIR/ty-param-fn-body.rs:30:5: 30:22, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-fn-body.rs:30:5 + | +30 | outlives(cell, t) + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr index 5b29ff8862109..426bece3e9de9 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 38 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#2r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn([]) } +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } --> $DIR/ty-param-fn.rs:22:5 | 22 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } --> $DIR/ty-param-fn.rs:38:5 | 38 | x From 5804637a81a8e5216fbbf2e3e7c0a7bdca9c99de Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 11:37:34 -0500 Subject: [PATCH 13/25] permit `ClosureOutlivesRequirement` to constrain regions or types --- src/librustc/ich/impls_mir.rs | 21 ++++++- src/librustc/mir/mod.rs | 33 +++++++--- src/librustc/ty/maps/mod.rs | 2 +- src/librustc_mir/borrow_check/mod.rs | 4 +- src/librustc_mir/borrow_check/nll/mod.rs | 11 +++- .../borrow_check/nll/region_infer/mod.rs | 63 ++++++++++++------- 6 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 32577ac46f3b1..df67c3abbe86c 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -536,14 +536,29 @@ impl<'gcx> HashStable> for mir::Literal<'gcx> { impl_stable_hash_for!(struct mir::Location { block, statement_index }); -impl_stable_hash_for!(struct mir::ClosureRegionRequirements { +impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> { num_external_vids, outlives_requirements }); -impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement { - free_region, +impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> { + subject, outlived_free_region, blame_span }); +impl<'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + mir::ClosureOutlivesSubject::Ty(ref ty) => { + ty.hash_stable(hcx, hasher); + } + mir::ClosureOutlivesSubject::Region(ref region) => { + region.hash_stable(hcx, hasher); + } + } + } +} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index a83c3f29d25bc..d7afce7de46c9 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1832,7 +1832,7 @@ pub struct GeneratorLayout<'tcx> { /// can be extracted from its type and constrained to have the given /// outlives relationship. #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub struct ClosureRegionRequirements { +pub struct ClosureRegionRequirements<'gcx> { /// The number of external regions defined on the closure. In our /// example above, it would be 3 -- one for `'static`, then `'1` /// and `'2`. This is just used for a sanity check later on, to @@ -1842,15 +1842,15 @@ pub struct ClosureRegionRequirements { /// Requirements between the various free regions defined in /// indices. - pub outlives_requirements: Vec, + pub outlives_requirements: Vec>, } -/// Indicates an outlives constraint between two free-regions declared -/// on the closure. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -pub struct ClosureOutlivesRequirement { - // This region ... - pub free_region: ty::RegionVid, +/// Indicates an outlives constraint between a type or between two +/// free-regions declared on the closure. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ClosureOutlivesRequirement<'tcx> { + // This region or type ... + pub subject: ClosureOutlivesSubject<'tcx>, // .. must outlive this one. pub outlived_free_region: ty::RegionVid, @@ -1859,6 +1859,23 @@ pub struct ClosureOutlivesRequirement { pub blame_span: Span, } +/// The subject of a ClosureOutlivesRequirement -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum ClosureOutlivesSubject<'tcx> { + /// Subject is a type, typically a type parameter, but could also + /// be a projection. Indicates a requirement like `T: 'a` being + /// passed to the caller, where the type here is `T`. + /// + /// The type here is guaranteed not to contain any free regions at + /// present. + Ty(Ty<'tcx>), + + /// Subject is a free region from the closure. Indicates a requirement + /// like `'a: 'b` being passed to the caller; the region here is `'a`. + Region(ty::RegionVid), +} + /* * TypeFoldable implementations for MIR types */ diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 7ba063adff4c2..319f63dd7c84a 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -193,7 +193,7 @@ define_maps! { <'tcx> /// Borrow checks the function body. If this is a closure, returns /// additional requirements that the closure's creator must verify. - [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option, + [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option>, /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 576d598579876..e0b03aec69a3b 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -65,7 +65,7 @@ pub fn provide(providers: &mut Providers) { fn mir_borrowck<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, -) -> Option { +) -> Option> { let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); @@ -89,7 +89,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, input_mir: &Mir<'gcx>, def_id: DefId, -) -> Option { +) -> Option> { let tcx = infcx.tcx; let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 9e7c94cd7ffb7..23fa2689053bf 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -9,11 +9,12 @@ // except according to those terms. use rustc::hir::def_id::DefId; -use rustc::mir::{ClosureRegionRequirements, Mir}; +use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir}; use rustc::infer::InferCtxt; use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; +use std::fmt::Debug; use std::io; use transform::MirSource; use util::liveness::{LivenessResults, LocalSet}; @@ -73,7 +74,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( move_data: &MoveData<'tcx>, ) -> ( RegionInferenceContext<'tcx>, - Option, + Option>, ) { // Run the MIR type-checker. let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); @@ -263,9 +264,13 @@ fn for_each_region_constraint( with_msg: &mut FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for req in &closure_region_requirements.outlives_requirements { + let subject: &Debug = match &req.subject { + ClosureOutlivesSubject::Region(subject) => subject, + ClosureOutlivesSubject::Ty(ty) => ty, + }; with_msg(&format!( "where {:?}: {:?}", - req.free_region, + subject, req.outlived_free_region, ))?; } 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 4759185d22623..4a1eca38bc7c6 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -15,7 +15,8 @@ use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; use rustc::infer::region_constraints::{GenericKind, VarOrigins}; -use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; +use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, + Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt; @@ -339,12 +340,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Perform region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. - pub(super) fn solve( + pub(super) fn solve<'gcx>( &mut self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, mir_def_id: DefId, - ) -> Option { + ) -> Option> { assert!(self.inferred_values.is_none(), "values already inferred"); self.propagate_constraints(mir); @@ -559,10 +560,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// If `propagated_outlives_requirements` is `Some`, then we will /// push unsatisfied obligations into there. Otherwise, we'll /// report them as errors. - fn check_universal_regions( + fn check_universal_regions<'gcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - mut propagated_outlives_requirements: Option<&mut Vec>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, ) { // The universal regions are always found in a prefix of the // full list. @@ -583,9 +584,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.extend(outlives_requirements.drain(..)); } else { for outlives_requirement in outlives_requirements.drain(..) { + let fr = match outlives_requirement.subject { + ClosureOutlivesSubject::Region(fr) => fr, + _ => span_bug!( + outlives_requirement.blame_span, + "check_universal_region() produced requirement w/ non-region subject" + ), + }; + self.report_error( infcx, - outlives_requirement.free_region, + fr, outlives_requirement.outlived_free_region, outlives_requirement.blame_span, ); @@ -602,11 +611,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Things that are to be propagated are accumulated into the /// `outlives_requirements` vector. - fn check_universal_region( + fn check_universal_region<'gcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, longer_fr: RegionVid, - propagated_outlives_requirements: &mut Vec, + propagated_outlives_requirements: &mut Vec>, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -645,7 +654,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Push the constraint `fr-: shorter_fr+` propagated_outlives_requirements.push(ClosureOutlivesRequirement { - free_region: fr_minus, + subject: ClosureOutlivesSubject::Region(fr_minus), outlived_free_region: shorter_fr_plus, blame_span: blame_span, }); @@ -773,7 +782,7 @@ pub trait ClosureRegionRequirementsExt { ); } -impl ClosureRegionRequirementsExt for ClosureRegionRequirements { +impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of @@ -815,17 +824,29 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { // Create the predicates. for outlives_requirement in &self.outlives_requirements { - let region = closure_mapping[outlives_requirement.free_region]; let outlived_region = closure_mapping[outlives_requirement.outlived_free_region]; - debug!( - "apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}", - region, - outlived_region, - outlives_requirement - ); + // FIXME, this origin is not entirely suitable. let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span); - infcx.sub_regions(origin, outlived_region, region); + + match outlives_requirement.subject { + ClosureOutlivesSubject::Region(region) => { + let region = closure_mapping[region]; + debug!( + "apply_requirements: region={:?} \ + outlived_region={:?} \ + outlives_requirements={:?}", + region, + outlived_region, + outlives_requirement + ); + infcx.sub_regions(origin, outlived_region, region); + } + + ClosureOutlivesSubject::Ty(_ty) => { + bug!("TODO not yet implemented -- closure outlives subject of a type"); + } + } } } } From 85e1d4749e5ba14ff89073f5812974ec8fdbffd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 13:04:38 -0500 Subject: [PATCH 14/25] propagate type tests from closure to closure creators Currently, we only propagate type tests that exclude all regions from the type. --- .../borrow_check/nll/region_infer/mod.rs | 157 ++++++++++++-- .../borrow_check/nll/type_check/mod.rs | 1 + .../borrow_check/nll/universal_regions.rs | 43 +++- ...param-closure-outlives-from-return-type.rs | 66 ++++++ ...m-closure-outlives-from-return-type.stderr | 58 ++++++ ...aram-closure-outlives-from-where-clause.rs | 96 +++++++++ ...-closure-outlives-from-where-clause.stderr | 191 ++++++++++++++++++ 7 files changed, 581 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr 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 4a1eca38bc7c6..244abb407982f 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -12,15 +12,18 @@ use super::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; +use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Location, Mir}; -use rustc::ty::{self, RegionVid}; +use rustc::traits::ObligationCause; +use rustc::ty::{self, RegionVid, TypeFoldable}; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt; use std::rc::Rc; +use syntax::ast; use syntax_pos::Span; mod annotation; @@ -361,7 +364,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { None }; - self.check_type_tests(infcx, mir); + self.check_type_tests(infcx, mir, outlives_requirements.as_mut()); self.check_universal_regions(infcx, outlives_requirements.as_mut()); @@ -439,21 +442,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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. - fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { + fn check_type_tests<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, + ) { let tcx = infcx.tcx; for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if self.eval_region_test( - mir, - type_test.point, - type_test.lower_bound, - &type_test.test, - ) { + if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) { continue; } + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { + if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) { + continue; + } + } + // Oh the humanity. Obviously we will do better than this error eventually. tcx.sess.span_err( type_test.span, @@ -462,6 +471,103 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + fn try_promote_type_test<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + type_test: &TypeTest<'tcx>, + propagated_outlives_requirements: &mut Vec>, + ) -> bool { + let tcx = infcx.tcx; + let gcx = tcx.global_tcx(); + + let TypeTest { + generic_kind, + lower_bound, + point: _, + span, + test: _, + } = type_test; + + // TODO. For now, just fail to promote anything with a + // region. This is obviously too strict: we will for example + // fail to promote `>::Bar` to our + // caller. But it is always sound not to promote, that just + // means more errors, and ignoring regions is a convenient + // starting point. This is because we would want to promote to + // a type that references the region-vids of the closure, for + // which we have no global representation just now. + let generic_ty = generic_kind.to_ty(tcx); + if generic_ty.has_free_regions() { + return false; + } + let generic_ty = gcx.lift(&generic_ty).unwrap(); + + // Find some bounding subject-region R+ that is a super-region + // of the existing subject-region R. This should be a non-local, universal + // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`. + let lower_bound_plus = self.promoted_type_test_bound(*lower_bound); + assert!(self.universal_regions.is_universal_region(lower_bound_plus)); + assert!(!self.universal_regions + .is_local_free_region(lower_bound_plus)); + + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Ty(generic_ty), + outlived_free_region: lower_bound_plus, + blame_span: *span, + }); + true + } + + /// Here, `lower_bound` (henceforth, `'r`) represents the bound from + /// some type-test `T: 'r`. We are a closure and have found that + /// `T: 'r` is not locally satisfiable, so we want to propagate + /// this constraint to our creator. It is sound for us to do so + /// with some `'r+` known to our creator, where `'r+: 'r`. + /// + /// The tricky bit here: this region `'r` may contain (a) any + /// number of points in the CFG and (b) any number of `end('x)` + /// elements of universally quantified regions. To communicate with + /// our creator, however, we have to pick exactly one universally + /// quantified region -- in other words, exactly one `end('x)` + /// element -- that they understand and which will be `'r+`. + /// + /// We do this as follows: + /// + /// - Ignore the CFG points in `'r`. All universally quantified regions + /// include the CFG anyhow. + /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding + /// a result `'y`. + /// - Finally, we take the non-local upper bound of `'y`. + fn promoted_type_test_bound(&self, lower_bound: RegionVid) -> RegionVid { + let inferred_values = self.inferred_values.as_ref().unwrap(); + + debug!( + "promoted_type_test_bound(lower_bound={:?}={})", + lower_bound, + inferred_values.region_value_str(lower_bound) + ); + + // Find the smallest universal region that contains all other + // universal regions within `region`. + let mut lub = self.universal_regions.fr_fn_body; + for ur in inferred_values.universal_regions_outlived_by(lower_bound) { + lub = self.universal_regions.postdom_upper_bound(lub, ur); + } + + debug!("promoted_type_test_bound: lub={:?}", lub); + + // Grow further to get smallest universal region known to + // creator. + let non_local_lub = self.universal_regions.non_local_upper_bound(lub); + + debug!( + "promoted_type_test_bound: non_local_lub={:?}", + non_local_lub + ); + + non_local_lub + } + /// Test if `test` is true when applied to `lower_bound` at /// `point`, and returns true or false. fn eval_region_test( @@ -487,13 +593,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { .iter() .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), - RegionTest::Any(tests) => tests.iter().any(|test| { - self.eval_region_test(mir, point, lower_bound, test) - }), + RegionTest::Any(tests) => tests + .iter() + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), - RegionTest::All(tests) => tests.iter().all(|test| { - self.eval_region_test(mir, point, lower_bound, test) - }), + RegionTest::All(tests) => tests + .iter() + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), } } @@ -772,17 +878,18 @@ impl fmt::Debug for Constraint { } } -pub trait ClosureRegionRequirementsExt { +pub trait ClosureRegionRequirementsExt<'gcx> { fn apply_requirements<'tcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, ); } -impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { +impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx> { /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of @@ -797,7 +904,8 @@ impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { /// requirements. fn apply_requirements<'tcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, @@ -843,8 +951,15 @@ impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { infcx.sub_regions(origin, outlived_region, region); } - ClosureOutlivesSubject::Ty(_ty) => { - bug!("TODO not yet implemented -- closure outlives subject of a type"); + ClosureOutlivesSubject::Ty(ty) => { + infcx.register_region_obligation( + body_id, + RegionObligation { + sup_type: ty, + sub_region: outlived_region, + cause: ObligationCause::misc(outlives_requirement.blame_span, body_id), + }, + ); } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index f7493f653e081..5c3cdbe2207ba 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1409,6 +1409,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) { closure_region_requirements.apply_requirements( self.infcx, + self.body_id, location, *def_id, *substs, diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index ce931a68a0c3b..d3e75626e63cf 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -267,6 +267,20 @@ impl<'tcx> UniversalRegions<'tcx> { self.num_universals } + /// Given two universal regions, returns the postdominating + /// upper-bound (effectively the least upper bound). + /// + /// (See `TransitiveRelation::postdom_upper_bound` for details on + /// the postdominating upper bound in general.) + pub fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid { + assert!(self.is_universal_region(fr1)); + assert!(self.is_universal_region(fr2)); + *self.relations + .inverse_outlives + .postdom_upper_bound(&fr1, &fr2) + .unwrap_or(&self.fr_static) + } + /// Finds an "upper bound" for `fr` that is not local. In other /// words, returns the smallest (*) known region `fr1` that (a) /// outlives `fr` and (b) is not local. This cannot fail, because @@ -431,7 +445,10 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // - `'r: 'fn_body` for every (other) universally quantified // region `'r`, all of which are provided by our caller for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) { - debug!("build: relating free region {:?} to itself and to 'static", fr); + debug!( + "build: relating free region {:?} to itself and to 'static", + fr + ); self.relations.relate_universal_regions(fr, fr); self.relations.relate_universal_regions(fr_static, fr); self.relations.relate_universal_regions(fr, fr_fn_body); @@ -442,15 +459,21 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // we should not have created any more variables assert_eq!(self.infcx.num_region_vars(), num_universals); - debug!("build: global regions = {}..{}", - FIRST_GLOBAL_INDEX, - first_extern_index); - debug!("build: extern regions = {}..{}", - first_extern_index, - first_local_index); - debug!("build: local regions = {}..{}", - first_local_index, - num_universals); + debug!( + "build: global regions = {}..{}", + FIRST_GLOBAL_INDEX, + first_extern_index + ); + debug!( + "build: extern regions = {}..{}", + first_extern_index, + first_local_index + ); + debug!( + "build: local regions = {}..{}", + first_local_index, + num_universals + ); UniversalRegions { indices, diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs new file mode 100644 index 0000000000000..6b23c82c77131 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -0,0 +1,66 @@ +// 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::fmt::Debug; + +fn with_signature<'a, T, F>(x: Box, op: F) -> Box + where F: FnOnce(Box) -> Box +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box) -> Box +where + T: Debug, +{ + // Here, the closure winds up being required to prove that `T: + // 'a`. In principle, it could know that, except that it is + // type-checked in a fully generic way, and hence it winds up with + // a propagated requirement that `T: '_#2`, where `'_#2` appears + // in the return type. The caller makes the mapping from `'_#2` to + // `'a` (and subsequently reports an error). + + with_signature(x, |y| y) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr new file mode 100644 index 0000000000000..721896a93ffcb --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -0,0 +1,58 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:27 + | +37 | with_signature(x, |y| y) + | ^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 + | +53 | x + | ^ + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 + | +37 | with_signature(x, |y| y) + | ^^^^^ + | + = note: defining type: DefId(0/1:14 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#4r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-return-type.rs:37:23: 37:28, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 + | +37 | with_signature(x, |y| y) + | ^^^^^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:1 + | +26 | / fn no_region<'a, T>(x: Box) -> Box +27 | | where +28 | | T: Debug, +29 | | { +... | +39 | | //~| ERROR failed type test +40 | | } + | |_^ + | + = note: defining type: DefId(0/0:5 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-closure-outlives-from-return-type.rs:53:5: 53:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } + --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 + | +53 | x + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs new file mode 100644 index 0000000000000..54f7b4fa50dfb --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -0,0 +1,96 @@ +// 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 can propagate `T: 'a` obligations to our caller. See +// `correct_region` for an explanation of how this test is setup; it's +// somewhat intricate. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn with_signature<'a, T, F>(a: Cell<&'a ()>, b: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(a, b) +} + +fn require<'a, T>(_a: &Cell<&'a ()>, _b: &T) +where + T: 'a, +{ +} + +#[rustc_regions] +fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { + with_signature(a, b, |x, y| { + //~^ ERROR failed type test + // + // See `correct_region`, which explains the point of this + // test. The only difference is that, in the case of this + // function, there is no where clause *anywhere*, and hence we + // get an error (but reported by the closure creator). + require(&x, &y) + //~^ WARNING not reporting region error due to -Znll + }) +} + +#[rustc_regions] +fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +where + T: 'a, +{ + with_signature(a, b, |x, y| { + // Key point of this test: + // + // The *closure* is being type-checked with all of its free + // regions "universalized". In particular, it does not know + // that `x` has the type `Cell<&'a ()>`, but rather treats it + // as if the type of `x` is `Cell<&'A ()>`, where `'A` is some + // fresh, independent region distinct from the `'a` which + // appears in the environment. The call to `require` here + // forces us then to prove that `T: 'A`, but the closure + // cannot do it on its own. It has to surface this requirement + // to its creator (which knows that `'a == 'A`). + require(&x, &y) + }) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, +{ + with_signature(a, b, |x, y| { + //~^ ERROR failed type test + // See `correct_region` + require(&x, &y) + //~^ WARNING not reporting region error due to -Znll + }) +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, + 'b: 'a, +{ + with_signature(a, b, |x, y| { + // See `correct_region` + require(&x, &y) + }) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr new file mode 100644 index 0000000000000..748333badce80 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -0,0 +1,191 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:45:9 + | +45 | require(&x, &y) + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:79:9 + | +79 | require(&x, &y) + | ^^^^^^^ + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 + | +38 | with_signature(a, b, |x, y| { + | __________________________^ +39 | | //~^ ERROR failed type test +40 | | // +41 | | // See `correct_region`, which explains the point of this +... | +46 | | //~^ WARNING not reporting region error due to -Znll +47 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:16 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:55:26 + | +55 | with_signature(a, b, |x, y| { + | __________________________^ +56 | | // Key point of this test: +57 | | // +58 | | // The *closure* is being type-checked with all of its free +... | +67 | | require(&x, &y) +68 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:19 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 + | +76 | with_signature(a, b, |x, y| { + | __________________________^ +77 | | //~^ ERROR failed type test +78 | | // See `correct_region` +79 | | require(&x, &y) +80 | | //~^ WARNING not reporting region error due to -Znll +81 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:23 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:90:26 + | +90 | with_signature(a, b, |x, y| { + | __________________________^ +91 | | // See `correct_region` +92 | | require(&x, &y) +93 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:27 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + +error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#3r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26: 47:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 + | +38 | with_signature(a, b, |x, y| { + | __________________________^ +39 | | //~^ ERROR failed type test +40 | | // +41 | | // See `correct_region`, which explains the point of this +... | +46 | | //~^ WARNING not reporting region error due to -Znll +47 | | }) + | |_____^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:37:1 + | +37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { +38 | | with_signature(a, b, |x, y| { +39 | | //~^ ERROR failed type test +40 | | // +... | +47 | | }) +48 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]) with substs [ + T + ] + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:51:1 + | +51 | / fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +52 | | where +53 | | T: 'a, +54 | | { +... | +68 | | }) +69 | | } + | |_^ + | + = note: defining type: DefId(0/0:7 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26: 81:6, test: IsOutlivedByAnyRegionIn(['_#1r, '_#3r]) } + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 + | +76 | with_signature(a, b, |x, y| { + | __________________________^ +77 | | //~^ ERROR failed type test +78 | | // See `correct_region` +79 | | require(&x, &y) +80 | | //~^ WARNING not reporting region error due to -Znll +81 | | }) + | |_____^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1 + | +72 | / fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +73 | | where +74 | | T: 'b, +75 | | { +... | +81 | | }) +82 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:85:1 + | +85 | / fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +86 | | where +87 | | T: 'b, +88 | | 'b: 'a, +... | +93 | | }) +94 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 2 previous errors + From 3a5842afe1e10023d84b7c1d44155c356f8ae4e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Dec 2017 05:18:58 -0500 Subject: [PATCH 15/25] add a new RegionKind variant: ReClosureBound This is needed to allow the `ClosureRegionRequirements` to capture types that include regions. --- src/librustc/ich/impls_ty.rs | 3 +++ src/librustc/infer/combine.rs | 8 ++++++++ src/librustc/infer/error_reporting/mod.rs | 8 ++++++++ src/librustc/infer/freshen.rs | 7 +++++++ src/librustc/infer/lexical_region_resolve/mod.rs | 7 ++++++- src/librustc/mir/mod.rs | 9 +++++++++ src/librustc/ty/sty.rs | 9 +++++++++ src/librustc/ty/util.rs | 2 ++ src/librustc/util/ppaux.rs | 8 ++++++++ src/librustc_borrowck/borrowck/gather_loans/mod.rs | 1 + src/librustc_mir/borrow_check/error_reporting.rs | 1 + src/librustc_typeck/variance/constraints.rs | 1 + src/librustdoc/clean/mod.rs | 1 + 13 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 2655e2acbbdfb..ea3a1074aa269 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -75,6 +75,9 @@ for ty::RegionKind { ty::ReFree(ref free_region) => { free_region.hash_stable(hcx, hasher); } + ty::ReClosureBound(vid) => { + vid.hash_stable(hcx, hasher); + } ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) => { diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 50a37e12531a7..f7bc092a3d7ae 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -475,6 +475,14 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' ty::Bivariant | ty::Covariant | ty::Contravariant => (), } } + + ty::ReClosureBound(..) => { + span_bug!( + self.span, + "encountered unexpected ReClosureBound: {:?}", + r, + ); + } } // FIXME: This is non-ideal because we don't give a diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 514b29120a96a..3e3aea0256e72 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -240,6 +240,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::ReErased => { (format!("lifetime {:?}", region), None) } + + // We shouldn't encounter an error message with ReClosureBound. + ty::ReClosureBound(..) => { + bug!( + "encountered unexpected ReClosureBound: {:?}", + region, + ); + } }; let message = format!("{}{}{}", prefix, description, suffix); if let Some(span) = span { diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 426c61e9ac083..1783d5abfc7c6 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -113,6 +113,13 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { // replace all free regions with 'erased self.tcx().types.re_erased } + + ty::ReClosureBound(..) => { + bug!( + "encountered unexpected ReClosureBound: {:?}", + r, + ); + } } } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 5a4f2157298b0..3ac4ec5bee416 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -258,7 +258,12 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.region_rels.tcx; match (a, b) { - (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + (&ty::ReClosureBound(..), _) | + (_, &ty::ReClosureBound(..)) | + (&ReLateBound(..), _) | + (_, &ReLateBound(..)) | + (&ReErased, _) | + (_, &ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d7afce7de46c9..dd3dd1e06de37 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1831,6 +1831,15 @@ pub struct GeneratorLayout<'tcx> { /// instance of the closure is created, the corresponding free regions /// can be extracted from its type and constrained to have the given /// outlives relationship. +/// +/// In some cases, we have to record outlives requirements between +/// types and regions as well. In that case, if those types include +/// any regions, those regions are recorded as `ReClosureBound` +/// instances assigned one of these same indices. Those regions will +/// be substituted away by the creator. We use `ReClosureBound` in +/// that case because the regions must be allocated in the global +/// TyCtxt, and hence we cannot use `ReVar` (which is what we use +/// internally within the rest of the NLL code). #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct ClosureRegionRequirements<'gcx> { /// The number of external regions defined on the closure. In our diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 2f72b3dde42f1..02729c6d60084 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1036,6 +1036,12 @@ pub enum RegionKind { /// Erased region, used by trait selection, in MIR and during trans. ReErased, + + /// These are regions bound in the "defining type" for a + /// closure. They are used ONLY as part of the + /// `ClosureRegionRequirements` that are produced by MIR borrowck. + /// See `ClosureRegionRequirements` for more details. + ReClosureBound(RegionVid), } impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {} @@ -1207,6 +1213,9 @@ impl RegionKind { } ty::ReErased => { } + ty::ReClosureBound(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } } match *self { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2e9e45c9ffe16..84d5f547f1b75 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -822,6 +822,8 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => { self.def_id(def_id); } + + ty::ReClosureBound(..) | ty::ReLateBound(..) | ty::ReFree(..) | ty::ReScope(..) | diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 9ff3d73f5c40e..5bfa646456857 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -733,6 +733,9 @@ define_print! { ty::ReErased => Ok(()), ty::ReStatic => write!(f, "'static"), ty::ReEmpty => write!(f, "'"), + + // The user should never encounter these in unsubstituted form. + ty::ReClosureBound(vid) => write!(f, "{:?}", vid), } } debug { @@ -743,6 +746,11 @@ define_print! { data.name) } + ty::ReClosureBound(ref vid) => { + write!(f, "ReClosureBound({:?})", + vid) + } + ty::ReLateBound(binder_id, ref bound_region) => { write!(f, "ReLateBound({:?}, {:?})", binder_id, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 8654f2a50e46b..5cbe2822e5c03 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -367,6 +367,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { ty::ReStatic => self.item_ub, ty::ReEmpty | + ty::ReClosureBound(..) | ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) | diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 31a94499fd0cc..2c0fa9878aaf2 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -381,6 +381,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { }, (RegionKind::ReLateBound(_, _), _) | (RegionKind::ReSkolemized(_, _), _) | + (RegionKind::ReClosureBound(_), _) | (RegionKind::ReErased, _) => { span_bug!(drop_span, "region does not make sense in this context"); }, diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index ef6552c8e33f4..df42d5eaa0a3d 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -465,6 +465,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::ReFree(..) | + ty::ReClosureBound(..) | ty::ReScope(..) | ty::ReVar(..) | ty::ReSkolemized(..) | diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 91908de98a65d..c7657c9b2ff45 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1055,6 +1055,7 @@ impl Clean> for ty::RegionKind { ty::ReVar(..) | ty::ReSkolemized(..) | ty::ReEmpty | + ty::ReClosureBound(_) | ty::ReErased => None } } From 3fcb13ae458ce6e2eedd34461b4e43c7be31b6b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Dec 2017 13:15:23 -0500 Subject: [PATCH 16/25] handle projections with regions --- .../borrow_check/nll/region_infer/mod.rs | 279 ++++++++++----- .../borrow_check/nll/universal_regions.rs | 17 +- .../projection-no-regions-closure.rs | 68 ++++ .../projection-no-regions-closure.stderr | 157 +++++++++ ...tion-fn.rs => projection-no-regions-fn.rs} | 2 +- ...stderr => projection-no-regions-fn.stderr} | 12 +- .../projection-one-region-closure.rs | 106 ++++++ .../projection-one-region-closure.stderr | 194 +++++++++++ ...ojection-one-region-trait-bound-closure.rs | 106 ++++++ ...tion-one-region-trait-bound-closure.stderr | 204 +++++++++++ ...n-one-region-trait-bound-static-closure.rs | 99 ++++++ ...e-region-trait-bound-static-closure.stderr | 155 +++++++++ ...ojection-two-region-trait-bound-closure.rs | 135 ++++++++ ...tion-two-region-trait-bound-closure.stderr | 326 ++++++++++++++++++ 14 files changed, 1758 insertions(+), 102 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr rename src/test/ui/nll/ty-outlives/{projection-fn.rs => projection-no-regions-fn.rs} (96%) rename src/test/ui/nll/ty-outlives/{projection-fn.stderr => projection-no-regions-fn.stderr} (54%) create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr 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 244abb407982f..f03e8bd7ac152 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -19,7 +19,7 @@ use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Location, Mir}; use rustc::traits::ObligationCause; -use rustc::ty::{self, RegionVid, TypeFoldable}; +use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt; use std::rc::Rc; @@ -478,7 +478,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements: &mut Vec>, ) -> bool { let tcx = infcx.tcx; - let gcx = tcx.global_tcx(); let TypeTest { generic_kind, @@ -488,80 +487,158 @@ impl<'tcx> RegionInferenceContext<'tcx> { test: _, } = type_test; - // TODO. For now, just fail to promote anything with a - // region. This is obviously too strict: we will for example - // fail to promote `>::Bar` to our - // caller. But it is always sound not to promote, that just - // means more errors, and ignoring regions is a convenient - // starting point. This is because we would want to promote to - // a type that references the region-vids of the closure, for - // which we have no global representation just now. let generic_ty = generic_kind.to_ty(tcx); - if generic_ty.has_free_regions() { - return false; - } - let generic_ty = gcx.lift(&generic_ty).unwrap(); + let subject = match self.try_promote_type_test_subject(infcx, generic_ty) { + Some(s) => s, + None => return false, + }; // Find some bounding subject-region R+ that is a super-region // of the existing subject-region R. This should be a non-local, universal // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`. - let lower_bound_plus = self.promoted_type_test_bound(*lower_bound); + let lower_bound_plus = self.non_local_universal_upper_bound(*lower_bound); assert!(self.universal_regions.is_universal_region(lower_bound_plus)); assert!(!self.universal_regions .is_local_free_region(lower_bound_plus)); propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Ty(generic_ty), + subject, outlived_free_region: lower_bound_plus, blame_span: *span, }); true } - /// Here, `lower_bound` (henceforth, `'r`) represents the bound from - /// some type-test `T: 'r`. We are a closure and have found that - /// `T: 'r` is not locally satisfiable, so we want to propagate - /// this constraint to our creator. It is sound for us to do so - /// with some `'r+` known to our creator, where `'r+: 'r`. + /// When we promote a type test `T: 'r`, we have to convert the + /// type `T` into something we can store in a query result (so + /// something allocated for `'gcx`). This is problematic if `ty` + /// contains regions. During the course of NLL region checking, we + /// will have replaced all of those regions with fresh inference + /// variables. To create a test subject, we want to replace those + /// inference variables with some region from the closure + /// signature -- this is not always possible, so this is a + /// fallible process. Presuming we do find a suitable region, we + /// will represent it with a `ReClosureBound`, which is a + /// `RegionKind` variant that can be allocated in the gcx. + fn try_promote_type_test_subject<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + let tcx = infcx.tcx; + let gcx = tcx.global_tcx(); + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + + debug!("try_promote_type_test_subject(ty = {:?})", ty); + + let ty = tcx.fold_regions(&ty, &mut false, |r, _depth| { + let region_vid = self.to_region_vid(r); + + // The challenge if this. We have some region variable `r` + // whose value is a set of CFG points and universal + // regions. We want to find if that set is *equivalent* to + // any of the named regions found in the closure. + // + // To do so, we compute the + // `non_local_universal_upper_bound`. This will be a + // non-local, universal region that is greater than `r`. + // However, it might not be *contained* within `r`, so + // then we further check whether this bound is contained + // in `r`. If so, we can say that `r` is equivalent to the + // bound. + // + // Let's work through a few examples. For these, imagine + // that we have 3 non-local regions (I'll denote them as + // `'static`, `'a`, and `'b`, though of course in the code + // they would be represented with indices) where: + // + // - `'static: 'a` + // - `'static: 'b` + // + // First, let's assume that `r` is some existential + // variable with an inferred value `{'a, 'static}` (plus + // some CFG nodes). In this case, the non-local upper + // bound is `'static`, since that outlives `'a`. `'static` + // is also a member of `r` and hence we consider `r` + // equivalent to `'static` (and replace it with + // `'static`). + // + // Now let's consider the inferred value `{'a, 'b}`. This + // means `r` is effectively `'a | 'b`. I'm not sure if + // this can come about, actually, but assuming it did, we + // would get a non-local upper bound of `'static`. Since + // `'static` is not contained in `r`, we would fail to + // find an equivalent. + let upper_bound = self.non_local_universal_upper_bound(region_vid); + if inferred_values.contains(region_vid, upper_bound) { + tcx.mk_region(ty::ReClosureBound(upper_bound)) + } else { + // In the case of a failure, use a `ReVar` + // result. This will cause the `lift` later on to + // fail. + r + } + }); + debug!("try_promote_type_test_subject: folded ty = {:?}", ty); + + // `lift` will only fail if we failed to promote some region. + let ty = gcx.lift(&ty)?; + + Some(ClosureOutlivesSubject::Ty(ty)) + } + + /// Given some universal or existential region `r`, finds a + /// non-local, universal region `r+` that outlives `r` at entry to (and + /// exit from) the closure. In the worst case, this will be + /// `'static`. /// - /// The tricky bit here: this region `'r` may contain (a) any - /// number of points in the CFG and (b) any number of `end('x)` - /// elements of universally quantified regions. To communicate with - /// our creator, however, we have to pick exactly one universally - /// quantified region -- in other words, exactly one `end('x)` - /// element -- that they understand and which will be `'r+`. + /// This is used for two purposes. First, if we are propagated + /// some requirement `T: r`, we can use this method to enlarge `r` + /// to something we can encode for our creator (which only knows + /// about non-local, universal regions). It is also used when + /// encoding `T` as part of `try_promote_type_test_subject` (see + /// that fn for details). /// - /// We do this as follows: + /// Since `r` is (potentially) an existential region, it has some + /// value which may include (a) any number of points in the CFG + /// and (b) any number of `end('x)` elements of universally + /// quantified regions. To convert this into a single universal + /// region we do as follows: /// /// - Ignore the CFG points in `'r`. All universally quantified regions /// include the CFG anyhow. /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. /// - Finally, we take the non-local upper bound of `'y`. - fn promoted_type_test_bound(&self, lower_bound: RegionVid) -> RegionVid { + /// - This uses `UniversalRegions::non_local_upper_bound`, which + /// is similar to this method but only works on universal + /// regions). + fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { let inferred_values = self.inferred_values.as_ref().unwrap(); debug!( - "promoted_type_test_bound(lower_bound={:?}={})", - lower_bound, - inferred_values.region_value_str(lower_bound) + "non_local_universal_upper_bound(r={:?}={})", + r, + inferred_values.region_value_str(r) ); // Find the smallest universal region that contains all other // universal regions within `region`. let mut lub = self.universal_regions.fr_fn_body; - for ur in inferred_values.universal_regions_outlived_by(lower_bound) { + for ur in inferred_values.universal_regions_outlived_by(r) { lub = self.universal_regions.postdom_upper_bound(lub, ur); } - debug!("promoted_type_test_bound: lub={:?}", lub); + debug!("non_local_universal_upper_bound: lub={:?}", lub); // Grow further to get smallest universal region known to // creator. let non_local_lub = self.universal_regions.non_local_upper_bound(lub); debug!( - "promoted_type_test_bound: non_local_lub={:?}", + "non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub ); @@ -680,32 +757,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Go through each of the universal regions `fr` and check that // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. - let mut outlives_requirements = vec![]; for (fr, _) in universal_definitions { - self.check_universal_region(infcx, fr, &mut outlives_requirements); - - // Propagate unsatisfied requirements if possible, else - // report them. - if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { - propagated_outlives_requirements.extend(outlives_requirements.drain(..)); - } else { - for outlives_requirement in outlives_requirements.drain(..) { - let fr = match outlives_requirement.subject { - ClosureOutlivesSubject::Region(fr) => fr, - _ => span_bug!( - outlives_requirement.blame_span, - "check_universal_region() produced requirement w/ non-region subject" - ), - }; - - self.report_error( - infcx, - fr, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); - } - } + self.check_universal_region(infcx, fr, &mut propagated_outlives_requirements); } } @@ -721,7 +774,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, longer_fr: RegionVid, - propagated_outlives_requirements: &mut Vec>, + propagated_outlives_requirements: &mut Option<&mut Vec>>, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -743,33 +796,39 @@ impl<'tcx> RegionInferenceContext<'tcx> { let blame_span = self.blame_span(longer_fr, shorter_fr); - // Shrink `fr` until we find a non-local region (if we do). - // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. - if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) { - debug!("check_universal_region: fr_minus={:?}", fr_minus); - - // Grow `shorter_fr` until we find a non-local - // regon. (We always will.) We'll call that - // `shorter_fr+` -- it's ever so slightly larger than - // `fr`. - let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr); - debug!( - "check_universal_region: shorter_fr_plus={:?}", - shorter_fr_plus - ); + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // Shrink `fr` until we find a non-local region (if we do). + // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. + if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) { + debug!("check_universal_region: fr_minus={:?}", fr_minus); + + // Grow `shorter_fr` until we find a non-local + // regon. (We always will.) We'll call that + // `shorter_fr+` -- it's ever so slightly larger than + // `fr`. + let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr); + debug!( + "check_universal_region: shorter_fr_plus={:?}", + shorter_fr_plus + ); - // Push the constraint `fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: shorter_fr_plus, - blame_span: blame_span, - }); - return; + // Push the constraint `fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: shorter_fr_plus, + blame_span: blame_span, + }); + return; + } } - // If we could not shrink `fr` to something smaller that - // the external users care about, then we can't pass the - // buck; just report an error. + // If we are not in a context where we can propagate + // errors, or we could not shrink `fr` to something + // smaller, then just report an error. + // + // Note: in this case, we use the unapproximated regions + // to report the error. This gives better error messages + // in some cases. self.report_error(infcx, longer_fr, shorter_fr, blame_span); } } @@ -878,8 +937,8 @@ impl fmt::Debug for Constraint { } } -pub trait ClosureRegionRequirementsExt<'gcx> { - fn apply_requirements<'tcx>( +pub trait ClosureRegionRequirementsExt<'gcx, 'tcx> { + fn apply_requirements( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, @@ -887,9 +946,18 @@ pub trait ClosureRegionRequirementsExt<'gcx> { closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, ); + + fn subst_closure_mapping( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + closure_mapping: &IndexVec>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>; } -impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx> { +impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequirements<'gcx> { /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of @@ -902,7 +970,7 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx /// a vector. Then we can just index into that vector to extract /// out the corresponding region from T and apply the /// requirements. - fn apply_requirements<'tcx>( + fn apply_requirements( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, @@ -927,7 +995,7 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx // into a vector. These are the regions that we will be // relating to one another. let closure_mapping = - UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids); + &UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids); debug!("apply_requirements: closure_mapping={:?}", closure_mapping); // Create the predicates. @@ -943,15 +1011,24 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx debug!( "apply_requirements: region={:?} \ outlived_region={:?} \ - outlives_requirements={:?}", + outlives_requirement={:?}", region, outlived_region, - outlives_requirement + outlives_requirement, ); infcx.sub_regions(origin, outlived_region, region); } ClosureOutlivesSubject::Ty(ty) => { + let ty = self.subst_closure_mapping(infcx, closure_mapping, &ty); + debug!( + "apply_requirements: ty={:?} \ + outlived_region={:?} \ + outlives_requirement={:?}", + ty, + outlived_region, + outlives_requirement, + ); infcx.register_region_obligation( body_id, RegionObligation { @@ -964,4 +1041,22 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx } } } + + fn subst_closure_mapping( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + closure_mapping: &IndexVec>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx> + { + infcx.tcx.fold_regions(value, &mut false, |r, _depth| { + if let ty::ReClosureBound(vid) = r { + closure_mapping[*vid] + } else { + bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) + } + }) + } } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index d3e75626e63cf..5f4a72542b20b 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -247,17 +247,24 @@ impl<'tcx> UniversalRegions<'tcx> { (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new) } - /// True if `r` is classied as a global region. + /// True if `r` is classified as a global region. pub fn is_global_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Global) } - /// True if `r` is classied as an external region. + /// True if `r` is classified as an external region. pub fn is_extern_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::External) } - /// True if `r` is classied as an local region. + /// True if `r` is a free region that is classified as global or + /// extern. This is an important category, because these regions + /// can be referenced in `ClosureRegionRequirements`. + pub fn is_non_local_free_region(&self, r: RegionVid) -> bool { + self.region_classification(r) == Some(RegionClassification::Local) + } + + /// True if `r` is classified as an local region. pub fn is_local_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Local) } @@ -324,6 +331,10 @@ impl<'tcx> UniversalRegions<'tcx> { relation: &TransitiveRelation, fr0: RegionVid, ) -> Option { + // This method assumes that `fr0` is one of the universally + // quantified region variables. + assert!(self.is_universal_region(fr0)); + let mut external_parents = vec![]; let mut queue = vec![&fr0]; diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs new file mode 100644 index 0000000000000..b91c01fb67139 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -0,0 +1,68 @@ +// 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +// Tests closures that propagate an outlives relationship to their +// creator where the subject is a projection with no regions (`::Item`, to be exact). + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +trait Anything { } + +impl Anything for T { } + +fn with_signature<'a, T, F>(x: Box, op: F) -> Box + where F: FnOnce(Box) -> Box +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box) -> Box +where + T: Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr new file mode 100644 index 0000000000000..1d124f2d49a0b --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -0,0 +1,157 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-closure.rs:36:31 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-closure.rs:54:31 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^ + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:36:23 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:15 ~ projection_no_regions_closure[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where ::Item: '_#2r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:46:23 + | +46 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:18 ~ projection_no_regions_closure[317d]::correct_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where ::Item: '_#2r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:54:23 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:22 ~ projection_no_regions_closure[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 4 + = note: where ::Item: '_#3r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:65:23 + | +65 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:26 ~ projection_no_regions_closure[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 4 + = note: where ::Item: '_#3r + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:36:23: 36:49, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-no-regions-closure.rs:36:23 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:32:1 + | +32 | / fn no_region<'a, T>(x: Box) -> Box +33 | | where +34 | | T: Iterator, +35 | | { +... | +38 | | //~| ERROR failed type test +39 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ projection_no_regions_closure[317d]::no_region[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:42:1 + | +42 | / fn correct_region<'a, T>(x: Box) -> Box +43 | | where +44 | | T: 'a + Iterator, +45 | | { +46 | | with_signature(x, |mut y| Box::new(y.next())) +47 | | } + | |_^ + | + = note: defining type: DefId(0/0:7 ~ projection_no_regions_closure[317d]::correct_region[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:54:23: 54:49, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } + --> $DIR/projection-no-regions-closure.rs:54:23 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:50:1 + | +50 | / fn wrong_region<'a, 'b, T>(x: Box) -> Box +51 | | where +52 | | T: 'b + Iterator, +53 | | { +... | +56 | | //~| ERROR failed type test +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_no_regions_closure[317d]::wrong_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:60:1 + | +60 | / fn outlives_region<'a, 'b, T>(x: Box) -> Box +61 | | where +62 | | T: 'b + Iterator, +63 | | 'b: 'a, +64 | | { +65 | | with_signature(x, |mut y| Box::new(y.next())) +66 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_no_regions_closure[317d]::outlives_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs similarity index 96% rename from src/test/ui/nll/ty-outlives/projection-fn.rs rename to src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs index 677d13527884a..b7822eb259b6d 100644 --- a/src/test/ui/nll/ty-outlives/projection-fn.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-Znll -Zborrowck=mir +// compile-flags:-Znll -Zborrowck=mir -Zverbose #![allow(warnings)] #![feature(dyn_trait)] diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr similarity index 54% rename from src/test/ui/nll/ty-outlives/projection-fn.stderr rename to src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr index 77eabef654566..5c3bd04f3b1e6 100644 --- a/src/test/ui/nll/ty-outlives/projection-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -1,23 +1,23 @@ warning: not reporting region error due to -Znll - --> $DIR/projection-fn.rs:24:5 + --> $DIR/projection-no-regions-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ warning: not reporting region error due to -Znll - --> $DIR/projection-fn.rs:40:5 + --> $DIR/projection-no-regions-fn.rs:40:5 | 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } - --> $DIR/projection-fn.rs:24:5 +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-no-regions-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } - --> $DIR/projection-fn.rs:40:5 +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } + --> $DIR/projection-no-regions-fn.rs:40:5 | 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs new file mode 100644 index 0000000000000..cd9b1c2a8cedd --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -0,0 +1,106 @@ +// 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 cases where we constrain `>::AssocType` to +// outlive `'a` and there are no bounds in the trait definition of +// `Anything`. This means that the constraint can only be satisfied in two +// ways: +// +// - by ensuring that `T: 'a` and `'b: 'a`, or +// - by something in the where clauses. +// +// As of this writing, the where clause option does not work because +// of limitations in our region inferencing system (this is true both +// with and without NLL). See `projection_outlives`. +// +// Ensuring that both `T: 'a` and `'b: 'a` holds does work (`elements_outlive`). + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T: 'a, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr new file mode 100644 index 0000000000000..d187a094ec629 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -0,0 +1,194 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:56:39 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:68:39 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:90:39 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-one-region-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + = note: where '_#1r: '_#2r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:68:29 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:90:29 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:103:29 + | +103 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:56:29: 56:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } + --> $DIR/projection-one-region-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-one-region-closure.rs:56:20 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:52:1 + | +52 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +53 | | where +54 | | T: Anything<'b>, +55 | | { +... | +59 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +60 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:68:29: 68:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } + --> $DIR/projection-one-region-closure.rs:68:29 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-closure.rs:68:20 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:63:1 + | +63 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +64 | | where +65 | | T: Anything<'b>, +66 | | 'a: 'a, +... | +71 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +72 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:90:29: 90:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } + --> $DIR/projection-one-region-closure.rs:90:29 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-closure.rs:90:20 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:75:1 + | +75 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +76 | | where +77 | | T: Anything<'b>, +78 | | T::AssocType: 'a, +... | +93 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +94 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:97:1 + | +97 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +98 | | where +99 | | T: Anything<'b>, +100 | | T: 'a, +... | +103 | | with_signature(cell, t, |cell, t| require(cell, t)); +104 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs new file mode 100644 index 0000000000000..e179927dfb0b9 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs @@ -0,0 +1,106 @@ +// 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 cases where we constrain `>::AssocType` to +// outlive `'a` and there is a unique bound in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'b`. In this +// case, the best way to satisfy the trait bound is to show that `'b: +// 'a`, which can be done in various ways. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'a; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr new file mode 100644 index 0000000000000..1088ae846fee5 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -0,0 +1,204 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:48:39 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:59:39 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:80:39 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:48:29 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:59:29 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:80:29 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:91:29 + | +91 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:103:29 + | +103 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-one-region-trait-bound-closure.rs:48:20 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:44:1 + | +44 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +45 | | where +46 | | T: Anything<'b>, +47 | | { +... | +50 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +51 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-trait-bound-closure.rs:59:20 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:54:1 + | +54 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +55 | | where +56 | | T: Anything<'b>, +57 | | 'a: 'a, +... | +61 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +62 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-trait-bound-closure.rs:80:20 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:65:1 + | +65 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +66 | | where +67 | | T: Anything<'b>, +68 | | T::AssocType: 'a, +... | +82 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +83 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:86:1 + | +86 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +87 | | where +88 | | T: Anything<'b>, +89 | | 'b: 'a, +90 | | { +91 | | with_signature(cell, t, |cell, t| require(cell, t)); +92 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:95:1 + | +95 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +96 | | where +97 | | T: Anything<'a>, +98 | | { +... | +103 | | with_signature(cell, t, |cell, t| require(cell, t)); +104 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs new file mode 100644 index 0000000000000..67e28af11469d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs @@ -0,0 +1,99 @@ +// 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 cases where we constrain `>::AssocType` to +// outlive `'static`. In this case, we don't get any errors, and in fact +// we don't even propagate constraints from the closures to the callers. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose +// must-compile-successfully + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'static; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr new file mode 100644 index 0000000000000..986676d28d920 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr @@ -0,0 +1,155 @@ +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:47:29 + | +47 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:75:29 + | +75 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:84:29 + | +84 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:96:29 + | +96 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:43:1 + | +43 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +44 | | where +45 | | T: Anything<'b>, +46 | | { +47 | | with_signature(cell, t, |cell, t| require(cell, t)); +48 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:51:1 + | +51 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +52 | | where +53 | | T: Anything<'b>, +54 | | 'a: 'a, +55 | | { +56 | | with_signature(cell, t, |cell, t| require(cell, t)); +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:60:1 + | +60 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +61 | | where +62 | | T: Anything<'b>, +63 | | T::AssocType: 'a, +... | +75 | | with_signature(cell, t, |cell, t| require(cell, t)); +76 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:79:1 + | +79 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +80 | | where +81 | | T: Anything<'b>, +82 | | 'b: 'a, +83 | | { +84 | | with_signature(cell, t, |cell, t| require(cell, t)); +85 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:88:1 + | +88 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +89 | | where +90 | | T: Anything<'a>, +91 | | { +... | +96 | | with_signature(cell, t, |cell, t| require(cell, t)); +97 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs new file mode 100644 index 0000000000000..f8f3065fff431 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -0,0 +1,135 @@ +// 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 cases where we constrain `>::AssocType` +// to outlive `'a` and there are two bounds in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'a` and +// `'b`. In this case, it's not clear what is the best way to satisfy +// the trait bound, and hence we propagate it to the caller as a type +// test. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a, 'b> { + type AssocType: 'a + 'b; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, 'c, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'c: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a, 'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr new file mode 100644 index 0000000000000..0265021412925 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -0,0 +1,326 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:49:39 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:60:39 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:81:39 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:109:39 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:22 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where >::AssocType: '_#3r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:32 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:92:29 + | +92 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:37 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:101:29 + | +101 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:42 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:109:29 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:46 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where >::AssocType: '_#2r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:120:29 + | +120 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:50 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where >::AssocType: '_#3r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:132:29 + | +132 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:53 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where >::AssocType: '_#2r + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#5r, '_#6r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#7r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:49:29: 49:55, test: Any([IsOutlivedByAnyRegionIn(['_#6r, '_#5r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#5r, '_#6r])])]) } + --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:45:1 + | +45 | / fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +46 | | where +47 | | T: Anything<'b, 'c>, +48 | | { +... | +51 | | //~| ERROR failed type test +52 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:60:29: 60:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } + --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:55:1 + | +55 | / fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +56 | | where +57 | | T: Anything<'b, 'c>, +58 | | 'a: 'a, +... | +62 | | //~| ERROR failed type test +63 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:81:29: 81:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } + --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:66:1 + | +66 | / fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +67 | | where +68 | | T: Anything<'b, 'c>, +69 | | T::AssocType: 'a, +... | +83 | | //~| ERROR failed type test +84 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:87:1 + | +87 | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +88 | | where +89 | | T: Anything<'b, 'c>, +90 | | 'b: 'a, +91 | | { +92 | | with_signature(cell, t, |cell, t| require(cell, t)); +93 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:96:1 + | +96 | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +97 | | where +98 | | T: Anything<'b, 'c>, +99 | | 'c: 'a, +100 | | { +101 | | with_signature(cell, t, |cell, t| require(cell, t)); +102 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-two-region-trait-bound-closure.rs:109:20 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:105:1 + | +105 | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +106 | | where +107 | | T: Anything<'b, 'b>, +108 | | { +... | +111 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +112 | | } + | |_^ + | + = note: defining type: DefId(0/0:13 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:115:1 + | +115 | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +116 | | where +117 | | T: Anything<'b, 'b>, +118 | | 'b: 'a, +119 | | { +120 | | with_signature(cell, t, |cell, t| require(cell, t)); +121 | | } + | |_^ + | + = note: defining type: DefId(0/0:14 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:124:1 + | +124 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +125 | | where +126 | | T: Anything<'a, 'a>, +127 | | { +... | +132 | | with_signature(cell, t, |cell, t| require(cell, t)); +133 | | } + | |_^ + | + = note: defining type: DefId(0/0:15 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to 4 previous errors + From e9824c50edd067fefb2e4fd6f6d1a4fe739a145b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 04:30:58 -0500 Subject: [PATCH 17/25] impose inputs/ouputs on MIR after the fact The input/output types found in `UniversalRegions` are not normalized. The old code used to assign them directly into the MIR, which would lead to errors when there was a projection in a argument or return type. This also led to some special cases in the `renumber` code. We now renumber uniformly but then pass the input/output types into the MIR type-checker, which equates them with the types found in MIR. This allows us to normalize at the same time. --- src/librustc_mir/borrow_check/nll/mod.rs | 4 +- src/librustc_mir/borrow_check/nll/renumber.rs | 68 ++----------------- .../borrow_check/nll/type_check/mod.rs | 49 ++++++++++++- .../borrow_check/nll/universal_regions.rs | 6 +- src/test/mir-opt/nll/named-lifetimes-basic.rs | 6 +- src/test/ui/nll/projection-return.rs | 29 ++++++++ 6 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 src/test/ui/nll/projection-return.rs diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 23fa2689053bf..6977d91d25a5d 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -53,7 +53,7 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( let universal_regions = UniversalRegions::new(infcx, def_id, param_env); // Replace all remaining regions with fresh inference variables. - renumber::renumber_mir(infcx, &universal_regions, mir); + renumber::renumber_mir(infcx, mir); let source = MirSource::item(def_id); mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(())); @@ -86,6 +86,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( param_env, mir, fr_fn_body, + universal_regions.input_tys, + universal_regions.output_ty, &liveness, flow_inits, move_data, diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 1262c238a132c..79505405692d8 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -8,50 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc_data_structures::indexed_vec::Idx; use rustc::ty::subst::Substs; use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; -use rustc::mir::RETURN_PLACE; +use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; -use super::ToRegionVid; -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>, - universal_regions: &UniversalRegions<'tcx>, - mir: &mut Mir<'tcx>, -) { +pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) { debug!("renumber_mir()"); debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); - // Update the return type and types of the arguments based on the - // `universal_regions` computation. - debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty); - mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty; - for (&input_ty, local) in universal_regions - .input_tys - .iter() - .zip((1..).map(Local::new)) - { - debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local); - mir.local_decls[local].ty = input_ty; - } - - let mut visitor = NLLVisitor { - infcx, - arg_count: mir.arg_count, - }; + let mut visitor = NLLVisitor { infcx }; visitor.visit_mir(mir); } struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - arg_count: usize, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { @@ -71,45 +45,13 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { self.infcx.next_nll_region_var(origin) }) } - - /// Checks that all the regions appearing in `value` have already - /// been renumbered. `FreeRegions` code should have done this. - fn assert_free_regions_are_renumbered(&self, value: &T) - where - T: TypeFoldable<'tcx>, - { - debug!("assert_free_regions_are_renumbered(value={:?})", value); - - self.infcx.tcx.for_each_free_region(value, |region| { - region.to_region_vid(); // will panic if `region` is not renumbered - }); - } - - fn is_argument_or_return_slot(&self, local: Local) -> bool { - // The first argument is return slot, next N are arguments. - local.index() <= self.arg_count - } } impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { - let is_arg = match ty_context { - TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local), - TyContext::ReturnTy(..) => true, - TyContext::Location(..) => false, - }; - debug!( - "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})", - ty, - is_arg, - ty_context - ); + debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context); - if is_arg { - self.assert_free_regions_are_renumbered(ty); - } else { - *ty = self.renumber_regions(ty_context, ty); - } + *ty = self.renumber_regions(ty_context, ty); debug!("visit_ty: ty={:?}", ty); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 5c3cdbe2207ba..6cdd77048c998 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -46,12 +46,31 @@ mod liveness; /// This phase of type-check ought to be infallible -- this is because /// the original, HIR-based type-check succeeded. So if any errors /// occur here, we will get a `bug!` reported. +/// +/// # Parameters +/// +/// - `infcx` -- inference context to use +/// - `body_id` -- body-id of the MIR being checked +/// - `param_env` -- parameter environment to use for trait solving +/// - `mir` -- MIR to type-check +/// - `implicit_region_bound` -- a region which all generic parameters are assumed +/// to outlive; should represent the fn body +/// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments; +/// the types of the input parameters found in the MIR itself will be equated with these +/// - `output_ty` -- fully liberaetd, but **not** normalized, expected return type; +/// the type for the RETURN_PLACE will be equated with this +/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness +/// constraints for the regions in the types of variables +/// - `flow_inits` -- results of a maybe-init dataflow analysis +/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis pub(crate) fn type_check<'gcx, 'tcx>( infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, implicit_region_bound: ty::Region<'tcx>, + input_tys: &[Ty<'tcx>], + output_ty: Ty<'tcx>, liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, @@ -62,7 +81,16 @@ pub(crate) fn type_check<'gcx, 'tcx>( param_env, mir, Some(implicit_region_bound), - &mut |cx| liveness::generate(cx, mir, liveness, flow_inits, move_data), + &mut |cx| { + liveness::generate(cx, mir, liveness, flow_inits, move_data); + + // Equate the input and output tys given by the user with + // the ones found in the MIR. + cx.equate_input_or_output(output_ty, mir.local_decls[RETURN_PLACE].ty); + for (&input_ty, local) in input_tys.iter().zip((1..).map(Local::new)) { + cx.equate_input_or_output(input_ty, mir.local_decls[local].ty); + } + }, ) } @@ -666,6 +694,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }) } + fn equate_input_or_output(&mut self, unnormalized_a: Ty<'tcx>, b: Ty<'tcx>) { + let start_position = Location { + block: START_BLOCK, + statement_index: 0, + }; + let a = self.normalize(&unnormalized_a, start_position); + if let Err(terr) = self.eq_types(a, b, start_position.at_self()) { + span_mirbug!( + self, + start_position, + "bad input or output {:?} normalized to {:?} should equal {:?} but got error {:?}", + unnormalized_a, + a, + b, + terr + ); + } + } + fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5f4a72542b20b..99beae13bd9e2 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -69,12 +69,14 @@ pub struct UniversalRegions<'tcx> { /// closure type, but for a top-level function it's the `TyFnDef`. pub defining_ty: Ty<'tcx>, - /// The return type of this function, with all regions replaced - /// by their universal `RegionVid` equivalents. + /// The return type of this function, with all regions replaced by + /// their universal `RegionVid` equivalents. This type is **NOT + /// NORMALIZED**. pub output_ty: Ty<'tcx>, /// The fully liberated input types of this function, with all /// regions replaced by their universal `RegionVid` equivalents. + /// This type is **NOT NORMALIZED**. pub input_tys: &'tcx [Ty<'tcx>], /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 71304f71b6101..f14979f973397 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -39,7 +39,11 @@ fn main() { // | '_#2r | {'_#2r, bb0[0], bb0[1]} // | '_#3r | {'_#3r, bb0[0], bb0[1]} // | '_#4r | {'_#4r, bb0[0], bb0[1]} +// | '_#5r | {'_#1r, bb0[0], bb0[1]} +// | '_#6r | {'_#2r, bb0[0], bb0[1]} +// | '_#7r | {'_#1r, bb0[0], bb0[1]} +// | '_#8r | {'_#3r, bb0[0], bb0[1]} // | // ... -// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { +// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool { // END rustc.use_x.nll.0.mir diff --git a/src/test/ui/nll/projection-return.rs b/src/test/ui/nll/projection-return.rs new file mode 100644 index 0000000000000..31388cf50c558 --- /dev/null +++ b/src/test/ui/nll/projection-return.rs @@ -0,0 +1,29 @@ +// 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. + +// compile-flags:-Znll -Zborrowck=mir +// must-compile-successfully + +#![feature(rustc_attrs)] + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = u32; +} + +fn foo() -> <() as Foo>::Bar { + 22 +} + +fn main() { } + From a118afe7ca742872f2064e26ddf97394d091ce3b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 04:56:03 -0500 Subject: [PATCH 18/25] add a test regarding relating closure and fn generics Turns out this works but we had no test targeting it. --- .../propagate-from-trait-match.rs | 59 ++++++++++++++++++ .../propagate-from-trait-match.stderr | 60 +++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs new file mode 100644 index 0000000000000..d5bd4b60118fc --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -0,0 +1,59 @@ +// 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 regions which appear only in the closure's generics (in +// this case, `'a`) are properly mapped to the creator's generics. In +// this case, the closure constrains its type parameter `T` to outlive +// the same `'a` for which it implements `Trait`, which can only be the `'a` +// from the function definition. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +trait Trait<'a> {} + +fn establish_relationships(value: T, closure: F) +where + F: FnOnce(T), +{ + closure(value) +} + +fn require<'a, T>(t: T) +where + T: Trait<'a> + 'a, +{ +} + +#[rustc_regions] +fn supply<'a, T>(value: T) +where + T: Trait<'a>, +{ + establish_relationships(value, |value| { + // This function call requires that + // + // (a) T: Trait<'a> + // + // and + // + // (b) T: 'a + // + // The latter does not hold. + + require(value); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr new file mode 100644 index 0000000000000..eb415ec8d1ae4 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -0,0 +1,60 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-from-trait-match.rs:53:9 + | +53 | require(value); + | ^^^^^^^ + +note: External requirements + --> $DIR/propagate-from-trait-match.rs:42:36 + | +42 | establish_relationships(value, |value| { + | ____________________________________^ +43 | | // This function call requires that +44 | | // +45 | | // (a) T: Trait<'a> +... | +55 | | //~| ERROR failed type test +56 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:16 ~ propagate_from_trait_match[317d]::supply[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((T,)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 56:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/propagate-from-trait-match.rs:42:36 + | +42 | establish_relationships(value, |value| { + | ____________________________________^ +43 | | // This function call requires that +44 | | // +45 | | // (a) T: Trait<'a> +... | +55 | | //~| ERROR failed type test +56 | | }); + | |_____^ + +note: No external requirements + --> $DIR/propagate-from-trait-match.rs:38:1 + | +38 | / fn supply<'a, T>(value: T) +39 | | where +40 | | T: Trait<'a>, +41 | | { +... | +56 | | }); +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to previous error + From 0d6bd42abb0b6082d23e7fe84b0121dfa0476b78 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 05:50:49 -0500 Subject: [PATCH 19/25] make `blame_span` deterministic --- .../borrow_check/nll/region_infer/mod.rs | 60 ++++++++++++------- .../compile-fail/mir_check_cast_closure.rs | 2 +- src/test/compile-fail/mir_check_cast_reify.rs | 2 +- .../compile-fail/mir_check_cast_unsafe_fn.rs | 2 +- .../propagate-approximated-fail-no-postdom.rs | 2 +- ...pagate-approximated-fail-no-postdom.stderr | 10 ++-- .../propagate-approximated-ref.stderr | 4 +- .../propagate-approximated-to-empty.stderr | 4 +- .../propagate-approximated-val.stderr | 4 +- .../propagate-from-trait-match.rs | 3 +- .../propagate-from-trait-match.stderr | 30 +++++----- 11 files changed, 71 insertions(+), 52 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 f03e8bd7ac152..9a3076c0c32b7 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -872,35 +872,50 @@ impl<'tcx> RegionInferenceContext<'tcx> { // 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 - ); + let relevant_constraint = self.constraints + .iter() + .filter_map(|constraint| { + if constraint.sub != fr2 { + None + } else { + influenced_fr1[constraint.sup] + .map(|distance| (distance, constraint.span)) + } + }) + .min() // constraining fr1 with fewer hops *ought* to be more obvious + .map(|(_dist, span)| span); + + relevant_constraint.unwrap_or_else(|| { + 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. + /// For each region, returns either `None` (does not influence + /// `'a`) or `Some(d)` which indicates that it influences `'a` + /// with distinct `d` (minimum number of edges that must be + /// traversed). /// /// Used during error reporting, extremely naive and inefficient. - fn dependencies(&self, r0: RegionVid) -> IndexVec { - let mut result_set = IndexVec::from_elem(false, &self.definitions); + fn dependencies(&self, r0: RegionVid) -> IndexVec> { + let mut result_set = IndexVec::from_elem(None, &self.definitions); let mut changed = true; - result_set[r0] = true; + result_set[r0] = Some(0); // distance 0 from `r0` while changed { changed = false; for constraint in &self.constraints { - if result_set[constraint.sup] { - if !result_set[constraint.sub] { - result_set[constraint.sub] = true; + if let Some(n) = result_set[constraint.sup] { + let m = n + 1; + if result_set[constraint.sub] + .map(|distance| m < distance) + .unwrap_or(true) + { + result_set[constraint.sub] = Some(m); changed = true; } } @@ -1049,13 +1064,16 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi value: &T, ) -> T where - T: TypeFoldable<'tcx> + T: TypeFoldable<'tcx>, { infcx.tcx.fold_regions(value, &mut false, |r, _depth| { if let ty::ReClosureBound(vid) = r { closure_mapping[*vid] } else { - bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) + bug!( + "subst_closure_mapping: encountered non-closure bound free region {:?}", + r + ) } }) } diff --git a/src/test/compile-fail/mir_check_cast_closure.rs b/src/test/compile-fail/mir_check_cast_closure.rs index be0d4b1374185..6562efeb6d893 100644 --- a/src/test/compile-fail/mir_check_cast_closure.rs +++ b/src/test/compile-fail/mir_check_cast_closure.rs @@ -14,9 +14,9 @@ fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 { let g: fn(_, _) -> _ = |_x, y| y; + //~^ ERROR free region `'b` does not outlive free region `'a` g //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `'b` does not outlive free region `'a` } fn main() {} diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/compile-fail/mir_check_cast_reify.rs index 091e0b71b2dc3..1736aea2d6de7 100644 --- a/src/test/compile-fail/mir_check_cast_reify.rs +++ b/src/test/compile-fail/mir_check_cast_reify.rs @@ -45,8 +45,8 @@ fn bar<'a>(x: &'a u32) -> &'static u32 { // as part of checking the `ReifyFnPointer`. let f: fn(_) -> _ = foo; //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#1r` does not outlive free region `'static` f(x) - //~^ ERROR free region `'_#1r` does not outlive free region `'static` } fn main() {} diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs index 701a7c6b056a6..39eafa1004026 100644 --- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs +++ b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs @@ -17,8 +17,8 @@ fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { // in `g`. These are related via the `UnsafeFnPointer` cast. let g: unsafe fn(_) -> _ = f; //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#1r` does not outlive free region `'static` unsafe { g(input) } - //~^ ERROR free region `'_#1r` does not outlive free region `'static` } fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs index c2f071cc029e6..50d7877de50d7 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs @@ -54,8 +54,8 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell // Only works if 'x: 'y: let p = x.get(); //~^ WARN not reporting region error due to -Znll + //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` demand_y(x, y, p) - //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r` }, ); } diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index cdda8ab5392bd..f90bc7c175a96 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -5,10 +5,10 @@ warning: not reporting region error due to -Znll | ^^^^^^^ error: free region `'_#5r` does not outlive free region `'_#6r` - --> $DIR/propagate-approximated-fail-no-postdom.rs:57:25 + --> $DIR/propagate-approximated-fail-no-postdom.rs:55:17 | -57 | demand_y(x, y, p) - | ^ +55 | let p = x.get(); + | ^ note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9 @@ -17,8 +17,8 @@ note: No external requirements 54 | | // Only works if 'x: 'y: 55 | | let p = x.get(); 56 | | //~^ WARN not reporting region error due to -Znll -57 | | demand_y(x, y, p) -58 | | //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r` +57 | | //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` +58 | | demand_y(x, y, p) 59 | | }, | |_________^ | diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 717cf481a01d1..4bae29ad32617 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -24,10 +24,10 @@ note: External requirements = note: where '_#1r: '_#2r error: free region `'_#1r` does not outlive free region `'_#2r` - --> $DIR/propagate-approximated-ref.rs:53:38 + --> $DIR/propagate-approximated-ref.rs:53:29 | 53 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ^^^^^^^ + | ^^^^^^^ note: No external requirements --> $DIR/propagate-approximated-ref.rs:52:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr index e8dc8a13f876b..502b344c89e44 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -5,10 +5,10 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#6r` does not outlive free region `'_#4r` - --> $DIR/propagate-approximated-to-empty.rs:41:21 + --> $DIR/propagate-approximated-to-empty.rs:41:18 | 41 | demand_y(x, y, x.get()) - | ^ + | ^ note: No external requirements --> $DIR/propagate-approximated-to-empty.rs:39:47 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 43464bfb2b9a7..43d61fdf1b5f8 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -24,10 +24,10 @@ note: External requirements = note: where '_#1r: '_#2r error: free region `'_#1r` does not outlive free region `'_#2r` - --> $DIR/propagate-approximated-val.rs:46:37 + --> $DIR/propagate-approximated-val.rs:46:29 | 46 | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { - | ^^^^^^ + | ^^^^^^ note: No external requirements --> $DIR/propagate-approximated-val.rs:45:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs index d5bd4b60118fc..a5be2b43f04bc 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -40,6 +40,8 @@ where T: Trait<'a>, { establish_relationships(value, |value| { + //~^ ERROR failed type test + // This function call requires that // // (a) T: Trait<'a> @@ -52,7 +54,6 @@ where require(value); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test }); } diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index eb415ec8d1ae4..e81c45ef7eda1 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -1,7 +1,7 @@ warning: not reporting region error due to -Znll - --> $DIR/propagate-from-trait-match.rs:53:9 + --> $DIR/propagate-from-trait-match.rs:55:9 | -53 | require(value); +55 | require(value); | ^^^^^^^ note: External requirements @@ -9,12 +9,12 @@ note: External requirements | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | // This function call requires that -44 | | // -45 | | // (a) T: Trait<'a> +43 | | //~^ ERROR failed type test +44 | | +45 | | // This function call requires that ... | -55 | | //~| ERROR failed type test -56 | | }); +56 | | //~^ WARNING not reporting region error due to -Znll +57 | | }); | |_____^ | = note: defining type: DefId(0/1:16 ~ propagate_from_trait_match[317d]::supply[0]::{{closure}}[0]) with closure substs [ @@ -26,17 +26,17 @@ note: External requirements = note: number of external vids: 2 = note: where T: '_#1r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 56:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 57:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } --> $DIR/propagate-from-trait-match.rs:42:36 | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | // This function call requires that -44 | | // -45 | | // (a) T: Trait<'a> +43 | | //~^ ERROR failed type test +44 | | +45 | | // This function call requires that ... | -55 | | //~| ERROR failed type test -56 | | }); +56 | | //~^ WARNING not reporting region error due to -Znll +57 | | }); | |_____^ note: No external requirements @@ -47,8 +47,8 @@ note: No external requirements 40 | | T: Trait<'a>, 41 | | { ... | -56 | | }); -57 | | } +57 | | }); +58 | | } | |_^ | = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [ From 0f8ef0ca1a298619001998fcb5c329159db206c7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 17:11:16 -0500 Subject: [PATCH 20/25] more concise debug output when dumping the value of a region --- .../borrow_check/nll/region_infer/values.rs | 63 ++++++++++++++++--- src/test/mir-opt/nll/named-lifetimes-basic.rs | 18 +++--- src/test/mir-opt/nll/reborrow-basic.rs | 4 +- src/test/mir-opt/nll/region-liveness-basic.rs | 4 +- .../nll/region-liveness-drop-may-dangle.rs | 2 +- .../nll/region-liveness-drop-no-may-dangle.rs | 2 +- .../nll/region-liveness-two-disjoint-uses.rs | 6 +- .../mir-opt/nll/region-subtyping-basic.rs | 6 +- 8 files changed, 77 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 849ccd3259a24..5f23a0e5790af 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -215,8 +215,11 @@ impl RegionValues { // FIXME. We could optimize this by improving // `BitMatrix::merge` so it does not always merge an entire // row. - debug!("add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", - from_region, to_region); + debug!( + "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", + from_region, + to_region + ); let mut changed = false; for elem in self.elements.all_universal_region_indices() { if self.contains(from_region, elem) { @@ -269,24 +272,70 @@ impl RegionValues { let mut result = String::new(); result.push_str("{"); - for (index, element) in self.elements_contained_in(r).enumerate() { - if index > 0 { - result.push_str(", "); - } + // Set to Some(l1, l2) when we have observed all the locations + // from l1..=l2 (inclusive) but not yet printed them. This + // gets extended if we then see l3 where l3 is the successor + // to l2. + let mut open_location: Option<(Location, Location)> = None; + + let mut sep = ""; + let mut push_sep = |s: &mut String| { + s.push_str(sep); + sep = ", "; + }; + for element in self.elements_contained_in(r) { match element { RegionElement::Location(l) => { - result.push_str(&format!("{:?}", l)); + if let Some((location1, location2)) = open_location { + if location2.block == l.block + && location2.statement_index == l.statement_index - 1 + { + open_location = Some((location1, l)); + continue; + } + + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + } + + open_location = Some((l, l)); } RegionElement::UniversalRegion(fr) => { + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + open_location = None; + } + + push_sep(&mut result); result.push_str(&format!("{:?}", fr)); } } } + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + } + result.push_str("}"); result } + + fn push_location_range(str: &mut String, location1: Location, location2: Location) { + if location1 == location2 { + str.push_str(&format!("{:?}", location1)); + } else { + assert_eq!(location1.block, location2.block); + str.push_str(&format!( + "{:?}[{}..={}]", + location1.block, + location1.statement_index, + location2.statement_index + )); + } + } } diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index f14979f973397..8feac15d69a82 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -34,15 +34,15 @@ fn main() { // | '_#4r | Local | ['_#4r] // | // | Inferred Region Values -// | '_#0r | {'_#0r, bb0[0], bb0[1]} -// | '_#1r | {'_#1r, bb0[0], bb0[1]} -// | '_#2r | {'_#2r, bb0[0], bb0[1]} -// | '_#3r | {'_#3r, bb0[0], bb0[1]} -// | '_#4r | {'_#4r, bb0[0], bb0[1]} -// | '_#5r | {'_#1r, bb0[0], bb0[1]} -// | '_#6r | {'_#2r, bb0[0], bb0[1]} -// | '_#7r | {'_#1r, bb0[0], bb0[1]} -// | '_#8r | {'_#3r, bb0[0], bb0[1]} +// | '_#0r | {'_#0r, bb0[0..=1]} +// | '_#1r | {'_#1r, bb0[0..=1]} +// | '_#2r | {'_#2r, bb0[0..=1]} +// | '_#3r | {'_#3r, bb0[0..=1]} +// | '_#4r | {'_#4r, bb0[0..=1]} +// | '_#5r | {'_#1r, bb0[0..=1]} +// | '_#6r | {'_#2r, bb0[0..=1]} +// | '_#7r | {'_#1r, bb0[0..=1]} +// | '_#8r | {'_#3r, bb0[0..=1]} // | // ... // fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool { diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index 9e6d6aaee15be..f69c51c3562dc 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,9 +28,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#7r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#7r | {bb0[6..=14]} // ... -// | '_#9r | {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#9r | {bb0[11..=14]} // ... // let _2: &'_#7r mut i32; // ... diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index ab99d79d7f388..e9834305550c3 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,8 +31,8 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} -// | '_#3r | {bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=1], bb3[0..=1]} +// | '_#3r | {bb2[1], bb3[0..=1]} // ... // let _2: &'_#3r usize; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index 515772a942711..c14ce6bb581de 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,7 +44,7 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// | '_#6r | {bb2[3..=5], bb3[0..=1]} // ... // let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index a257910b0b80b..058a57fe612cf 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,7 +46,7 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// | '_#6r | {bb2[3..=5], bb3[0..=2], bb4[0], bb5[0..=2], bb6[0], bb7[0..=1], bb8[0]} // ... // let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir 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 3041c2cb06162..821cd73667193 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,10 +36,10 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=1], bb3[0..=1]} // ... -// | '_#4r | {bb8[1], bb8[2], bb8[3], bb8[4]} -// | '_#5r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} +// | '_#4r | {bb8[1..=4]} +// | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]} // ... // let mut _2: &'_#5r usize; // ... diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 5a3f831331c43..a3f68ed5ebf5e 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,9 +32,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} -// | '_#3r | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} -// | '_#4r | {bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=6], bb3[0..=1]} +// | '_#3r | {bb2[1..=6], bb3[0..=1]} +// | '_#4r | {bb2[5..=6], bb3[0..=1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // let _2: &'_#3r usize; From 4f43c5b1e78115083254e6df12e0d34f7da9748b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 15 Dec 2017 17:04:48 -0500 Subject: [PATCH 21/25] stop dumping DefPath into "failed type test" errors The prior messages were not stable across platforms. --- .../borrow_check/nll/region_infer/mod.rs | 6 +++++- .../propagate-from-trait-match.rs | 2 +- .../propagate-from-trait-match.stderr | 6 +++--- .../ty-outlives/projection-no-regions-closure.rs | 4 ++-- .../projection-no-regions-closure.stderr | 8 ++++---- .../ui/nll/ty-outlives/projection-no-regions-fn.rs | 4 ++-- .../ty-outlives/projection-no-regions-fn.stderr | 4 ++-- .../ty-outlives/projection-one-region-closure.rs | 6 +++--- .../projection-one-region-closure.stderr | 6 +++--- .../projection-two-region-trait-bound-closure.rs | 6 +++--- ...rojection-two-region-trait-bound-closure.stderr | 12 ++++++------ .../ty-param-closure-outlives-from-return-type.rs | 4 ++-- ...-param-closure-outlives-from-return-type.stderr | 6 +++--- .../ty-param-closure-outlives-from-where-clause.rs | 4 ++-- ...param-closure-outlives-from-where-clause.stderr | 14 +++++++------- src/test/ui/nll/ty-outlives/ty-param-fn-body.rs | 2 +- .../ui/nll/ty-outlives/ty-param-fn-body.stderr | 2 +- src/test/ui/nll/ty-outlives/ty-param-fn.rs | 4 ++-- src/test/ui/nll/ty-outlives/ty-param-fn.stderr | 4 ++-- 19 files changed, 54 insertions(+), 50 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 9a3076c0c32b7..36b59d9fb7d33 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -466,7 +466,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Oh the humanity. Obviously we will do better than this error eventually. tcx.sess.span_err( type_test.span, - &format!("failed type test: {:?}", type_test), + &format!( + "`{}` does not outlive `{:?}`", + type_test.generic_kind, + type_test.lower_bound, + ), ); } } diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs index a5be2b43f04bc..604c81da49db3 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -40,7 +40,7 @@ where T: Trait<'a>, { establish_relationships(value, |value| { - //~^ ERROR failed type test + //~^ ERROR `T` does not outlive // This function call requires that // diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index e81c45ef7eda1..efac55f2beeac 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -9,7 +9,7 @@ note: External requirements | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | //~^ ERROR failed type test +43 | | //~^ ERROR `T` does not outlive 44 | | 45 | | // This function call requires that ... | @@ -26,12 +26,12 @@ note: External requirements = note: number of external vids: 2 = note: where T: '_#1r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 57:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#3r` --> $DIR/propagate-from-trait-match.rs:42:36 | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | //~^ ERROR failed type test +43 | | //~^ ERROR `T` does not outlive 44 | | 45 | | // This function call requires that ... | diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs index b91c01fb67139..9451163ace993 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -35,7 +35,7 @@ where { with_signature(x, |mut y| Box::new(y.next())) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } #[rustc_regions] @@ -53,7 +53,7 @@ where { with_signature(x, |mut y| Box::new(y.next())) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 1d124f2d49a0b..9afd5d41182f4 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -72,7 +72,7 @@ note: External requirements = note: number of external vids: 4 = note: where ::Item: '_#3r -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:36:23: 36:49, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `::Item` does not outlive `'_#4r` --> $DIR/projection-no-regions-closure.rs:36:23 | 36 | with_signature(x, |mut y| Box::new(y.next())) @@ -86,7 +86,7 @@ note: No external requirements 34 | | T: Iterator, 35 | | { ... | -38 | | //~| ERROR failed type test +38 | | //~| ERROR `::Item` does not outlive 39 | | } | |_^ | @@ -111,7 +111,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:54:23: 54:49, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `::Item` does not outlive `'_#6r` --> $DIR/projection-no-regions-closure.rs:54:23 | 54 | with_signature(x, |mut y| Box::new(y.next())) @@ -125,7 +125,7 @@ note: No external requirements 52 | | T: 'b + Iterator, 53 | | { ... | -56 | | //~| ERROR failed type test +56 | | //~| ERROR `::Item` does not outlive 57 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs index b7822eb259b6d..c815fdc1a0c5e 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -23,7 +23,7 @@ where { Box::new(x.next()) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } fn correct_region<'a, T>(mut x: T) -> Box @@ -39,7 +39,7 @@ where { Box::new(x.next()) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } fn outlives_region<'a, 'b, T>(mut x: T) -> Box diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr index 5c3bd04f3b1e6..4d13972641c16 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `::Item` does not outlive `'_#4r` --> $DIR/projection-no-regions-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `::Item` does not outlive `'_#5r` --> $DIR/projection-no-regions-fn.rs:40:5 | 40 | Box::new(x.next()) diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs index cd9b1c2a8cedd..e2a2d20d77d5c 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -55,7 +55,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` } @@ -67,7 +67,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` } @@ -89,7 +89,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` } diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index d187a094ec629..cbd80d70bf955 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -83,7 +83,7 @@ note: External requirements = note: where T: '_#3r = note: where '_#2r: '_#3r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:56:29: 56:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } +error: `T` does not outlive `'_#5r` --> $DIR/projection-one-region-closure.rs:56:29 | 56 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -112,7 +112,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:68:29: 68:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } +error: `T` does not outlive `'_#6r` --> $DIR/projection-one-region-closure.rs:68:29 | 68 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -142,7 +142,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:90:29: 90:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } +error: `T` does not outlive `'_#6r` --> $DIR/projection-one-region-closure.rs:90:29 | 90 | with_signature(cell, t, |cell, t| require(cell, t)); diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs index f8f3065fff431..42bfdfcf9f91b 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -48,7 +48,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `>::AssocType` does not outlive `'_#7r` } #[rustc_regions] @@ -59,7 +59,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `>::AssocType` does not outlive `'_#8r` } #[rustc_regions] @@ -80,7 +80,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `>::AssocType` does not outlive `'_#8r` } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index 0265021412925..5b708a0d7e629 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -152,7 +152,7 @@ note: External requirements = note: number of external vids: 3 = note: where >::AssocType: '_#2r -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#5r, '_#6r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#7r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:49:29: 49:55, test: Any([IsOutlivedByAnyRegionIn(['_#6r, '_#5r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#5r, '_#6r])])]) } +error: `>::AssocType` does not outlive `'_#7r` --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 | 49 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -166,7 +166,7 @@ note: No external requirements 47 | | T: Anything<'b, 'c>, 48 | | { ... | -51 | | //~| ERROR failed type test +51 | | //~| ERROR `>::AssocType` does not outlive `'_#7r` 52 | | } | |_^ | @@ -176,7 +176,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:60:29: 60:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } +error: `>::AssocType` does not outlive `'_#8r` --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 | 60 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -190,7 +190,7 @@ note: No external requirements 57 | | T: Anything<'b, 'c>, 58 | | 'a: 'a, ... | -62 | | //~| ERROR failed type test +62 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` 63 | | } | |_^ | @@ -201,7 +201,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:81:29: 81:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } +error: `>::AssocType` does not outlive `'_#8r` --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 | 81 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -215,7 +215,7 @@ note: No external requirements 68 | | T: Anything<'b, 'c>, 69 | | T::AssocType: 'a, ... | -83 | | //~| ERROR failed type test +83 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` 84 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs index 6b23c82c77131..14e2eb26976ab 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -36,7 +36,7 @@ where with_signature(x, |y| y) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn correct_region<'a, T>(x: Box) -> Box @@ -52,7 +52,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn outlives_region<'a, 'b, T>(x: Box) -> Box diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr index 721896a93ffcb..37ebc38da4dae 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -25,7 +25,7 @@ note: External requirements = note: number of external vids: 3 = note: where T: '_#2r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#4r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-return-type.rs:37:23: 37:28, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 | 37 | with_signature(x, |y| y) @@ -39,7 +39,7 @@ note: No external requirements 28 | | T: Debug, 29 | | { ... | -39 | | //~| ERROR failed type test +39 | | //~| ERROR `T` does not outlive 40 | | } | |_^ | @@ -48,7 +48,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-closure-outlives-from-return-type.rs:53:5: 53:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 | 53 | x diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs index 54f7b4fa50dfb..beed1a740eac3 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -36,7 +36,7 @@ where #[rustc_regions] fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { with_signature(a, b, |x, y| { - //~^ ERROR failed type test + //~^ ERROR `T` does not outlive // // See `correct_region`, which explains the point of this // test. The only difference is that, in the case of this @@ -74,7 +74,7 @@ where T: 'b, { with_signature(a, b, |x, y| { - //~^ ERROR failed type test + //~^ ERROR `T` does not outlive // See `correct_region` require(&x, &y) //~^ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr index 748333badce80..78445eb47c328 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -15,7 +15,7 @@ note: External requirements | 38 | with_signature(a, b, |x, y| { | __________________________^ -39 | | //~^ ERROR failed type test +39 | | //~^ ERROR `T` does not outlive 40 | | // 41 | | // See `correct_region`, which explains the point of this ... | @@ -58,7 +58,7 @@ note: External requirements | 76 | with_signature(a, b, |x, y| { | __________________________^ -77 | | //~^ ERROR failed type test +77 | | //~^ ERROR `T` does not outlive 78 | | // See `correct_region` 79 | | require(&x, &y) 80 | | //~^ WARNING not reporting region error due to -Znll @@ -94,12 +94,12 @@ note: External requirements = note: number of external vids: 4 = note: where T: '_#3r -error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#3r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26: 47:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#3r` --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 | 38 | with_signature(a, b, |x, y| { | __________________________^ -39 | | //~^ ERROR failed type test +39 | | //~^ ERROR `T` does not outlive 40 | | // 41 | | // See `correct_region`, which explains the point of this ... | @@ -112,7 +112,7 @@ note: No external requirements | 37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { 38 | | with_signature(a, b, |x, y| { -39 | | //~^ ERROR failed type test +39 | | //~^ ERROR `T` does not outlive 40 | | // ... | 47 | | }) @@ -140,12 +140,12 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26: 81:6, test: IsOutlivedByAnyRegionIn(['_#1r, '_#3r]) } +error: `T` does not outlive `'_#5r` --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 | 76 | with_signature(a, b, |x, y| { | __________________________^ -77 | | //~^ ERROR failed type test +77 | | //~^ ERROR `T` does not outlive 78 | | // See `correct_region` 79 | | require(&x, &y) 80 | | //~^ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs index 42e62b2389685..a1e636cbc444b 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -29,7 +29,7 @@ fn region_within_body(t: T) { fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { outlives(cell, t) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn outlives<'a, T>(x: Cell<&'a usize>, y: T) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr index 3cc335bb29db2..bbe55c52b6ed5 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 30 | outlives(cell, t) | ^^^^^^^^ -error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#4r, point: bb0[4], span: $DIR/ty-param-fn-body.rs:30:5: 30:22, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-fn-body.rs:30:5 | 30 | outlives(cell, t) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs index c6547ae68fa1e..76783af4ceb0e 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -21,7 +21,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn correct_region<'a, T>(x: Box) -> Box @@ -37,7 +37,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn outlives_region<'a, 'b, T>(x: Box) -> Box diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr index 426bece3e9de9..02c4ebbd5aca9 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 38 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#3r` --> $DIR/ty-param-fn.rs:22:5 | 22 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-fn.rs:38:5 | 38 | x From 03bfb0f31670364a47ddf14db068bf47e291e6ba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:18:15 -0500 Subject: [PATCH 22/25] tweak comment on `TypeTest` to be more accurate --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 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 36b59d9fb7d33..e2a36e6863ee2 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -122,10 +122,12 @@ pub struct Constraint { /// checks that they meet certain extra criteria. If not, an error /// can be issued. /// -/// One reason for this is that these type tests always boil down to a -/// check like `'a: 'x` where `'a` is a universally quantified region -/// -- and therefore not one whose value is really meant to be -/// *inferred*, precisely. Another reason is that these type tests can +/// One reason for this is that these type tests typically boil down +/// to a check like `'a: 'x` where `'a` is a universally quantified +/// region -- and therefore not one whose value is really meant to be +/// *inferred*, precisely (this is not always the case: one can have a +/// type test like `>::Bar: 'x`, where `'?0` is an +/// inference variable). Another reason is that these type tests can /// involve *disjunction* -- that is, they can be satisfied in more /// than one way. /// From 3c56c3610ec7684f4c759c3eee95a50fef208e1b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:21:57 -0500 Subject: [PATCH 23/25] fix comment on `check_type_tests` --- .../borrow_check/nll/region_infer/mod.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 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 e2a36e6863ee2..58e16e7673afc 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -432,18 +432,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Once regions have been propagated, this method is used to 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 } - /// - /// 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. + /// whether the "type tests" produced by typeck were satisfied; + /// type tests encode type-outlives relationships like `T: + /// 'a`. See `TypeTest` for more details. fn check_type_tests<'gcx>( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, From 3d826e5681770a2dfee4b980173f383b8201e844 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:26:56 -0500 Subject: [PATCH 24/25] remove dead `is_foo_free_region` helpers Only `is_local_free_region` is used. --- .../borrow_check/nll/universal_regions.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 99beae13bd9e2..64195338aa5e8 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -249,23 +249,6 @@ impl<'tcx> UniversalRegions<'tcx> { (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new) } - /// True if `r` is classified as a global region. - pub fn is_global_free_region(&self, r: RegionVid) -> bool { - self.region_classification(r) == Some(RegionClassification::Global) - } - - /// True if `r` is classified as an external region. - pub fn is_extern_free_region(&self, r: RegionVid) -> bool { - self.region_classification(r) == Some(RegionClassification::External) - } - - /// True if `r` is a free region that is classified as global or - /// extern. This is an important category, because these regions - /// can be referenced in `ClosureRegionRequirements`. - pub fn is_non_local_free_region(&self, r: RegionVid) -> bool { - self.region_classification(r) == Some(RegionClassification::Local) - } - /// True if `r` is classified as an local region. pub fn is_local_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Local) From 1816ede386c6dd6e61f50e7b0f9bdba19adc0e24 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:28:45 -0500 Subject: [PATCH 25/25] be specific about what kind of normalization we mean --- src/librustc_mir/borrow_check/nll/universal_regions.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 64195338aa5e8..a1e6ea135c68d 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -71,12 +71,14 @@ pub struct UniversalRegions<'tcx> { /// The return type of this function, with all regions replaced by /// their universal `RegionVid` equivalents. This type is **NOT - /// NORMALIZED**. + /// NORMALIZED** (i.e., it contains unnormalized associated type + /// projections). pub output_ty: Ty<'tcx>, /// The fully liberated input types of this function, with all /// regions replaced by their universal `RegionVid` equivalents. - /// This type is **NOT NORMALIZED**. + /// This type is **NOT NORMALIZED** (i.e., it contains + /// unnormalized associated type projections). pub input_tys: &'tcx [Ty<'tcx>], /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to