diff --git a/src/Cargo.lock b/src/Cargo.lock index 8594e4ff1306d..09baaeadaee43 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -87,7 +87,7 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.11" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -187,7 +187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "cargo" version = "0.30.0" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -303,7 +303,7 @@ version = "2.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -622,7 +622,7 @@ name = "env_logger" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1844,7 +1844,7 @@ name = "rustc-ap-rustc_errors" version = "182.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-rustc_data_structures 182.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-serialize 182.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-syntax_pos 182.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2097,7 +2097,7 @@ dependencies = [ name = "rustc_errors" version = "0.0.0" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_data_structures 0.0.0", "serialize 0.0.0", "syntax_pos 0.0.0", @@ -3056,7 +3056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum assert_cli 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98589b0e465a6c510d95fceebd365bb79bedece7f6e18a480897f2015f85ec51" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 2390d7eccce76..1721d1dd0e9c5 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -39,7 +39,6 @@ pub struct DepGraph { fingerprints: Lrc>> } - newtype_index!(DepNodeIndex); impl DepNodeIndex { diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 43ed80b474ad5..cb4e1ab65e759 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -617,6 +617,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { debug!("leak_check: skol_map={:?}", skol_map); + // If the user gave `-Zno-leak-check`, then skip the leak + // check completely. This is wildly unsound and also not + // unlikely to cause an ICE or two. It is intended for use + // only during a transition period, in which the MIR typeck + // uses the "universe-style" check, and the rest of typeck + // uses the more conservative leak check. Since the leak + // check is more conservative, we can't test the + // universe-style check without disabling it. + if self.tcx.sess.opts.debugging_opts.no_leak_check { + return Ok(()); + } + let new_vars = self.region_vars_confined_to_snapshot(snapshot); for (&skol_br, &skol) in skol_map { // The inputs to a skolemized variable can only diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 9283705b7b78e..0b84c6a0aa77a 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -377,7 +377,23 @@ pub enum NLLRegionVariableOrigin { // elsewhere. This origin indices we've got one of those. FreeRegion, - Inferred(::mir::visit::TyContext), + BoundRegion(ty::UniverseIndex), + + Existential, +} + +impl NLLRegionVariableOrigin { + pub fn is_universal(self) -> bool { + match self { + NLLRegionVariableOrigin::FreeRegion => true, + NLLRegionVariableOrigin::BoundRegion(..) => true, + NLLRegionVariableOrigin::Existential => false, + } + } + + pub fn is_existential(self) -> bool { + !self.is_universal() + } } #[derive(Copy, Clone, Debug)] @@ -1381,6 +1397,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn universe(&self) -> ty::UniverseIndex { self.universe.get() } + + /// Create and return a new subunivese of the current universe; + /// update `self.universe` to that new subuniverse. At present, + /// used only in the NLL subtyping code, which uses the new + /// universe-based scheme instead of the more limited leak-check + /// scheme. + pub fn create_subuniverse(&self) -> ty::UniverseIndex { + let u = self.universe.get().subuniverse(); + self.universe.set(u); + u + } } impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index c72952efc6144..d709a4debd1d3 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -69,6 +69,7 @@ #![feature(trusted_len)] #![feature(vec_remove_item)] #![feature(catch_expr)] +#![feature(step_trait)] #![feature(integer_atomics)] #![feature(test)] #![feature(in_band_lifetimes)] diff --git a/src/librustc/mir/traversal.rs b/src/librustc/mir/traversal.rs index 92888ed99e472..87ba3420eeceb 100644 --- a/src/librustc/mir/traversal.rs +++ b/src/librustc/mir/traversal.rs @@ -9,7 +9,6 @@ // except according to those terms. use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::Idx; use super::*; @@ -33,7 +32,7 @@ use super::*; #[derive(Clone)] pub struct Preorder<'a, 'tcx: 'a> { mir: &'a Mir<'tcx>, - visited: BitVector, + visited: BitVector, worklist: Vec, } @@ -58,7 +57,7 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { while let Some(idx) = self.worklist.pop() { - if !self.visited.insert(idx.index()) { + if !self.visited.insert(idx) { continue; } @@ -107,7 +106,7 @@ impl<'a, 'tcx> ExactSizeIterator for Preorder<'a, 'tcx> {} /// A Postorder traversal of this graph is `D B C A` or `D C B A` pub struct Postorder<'a, 'tcx: 'a> { mir: &'a Mir<'tcx>, - visited: BitVector, + visited: BitVector, visit_stack: Vec<(BasicBlock, Successors<'a>)> } @@ -123,7 +122,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { let data = &po.mir[root]; if let Some(ref term) = data.terminator { - po.visited.insert(root.index()); + po.visited.insert(root); po.visit_stack.push((root, term.successors())); po.traverse_successor(); } @@ -190,8 +189,8 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { break; }; - if self.visited.insert(bb.index()) { - if let Some(ref term) = self.mir[bb].terminator { + if self.visited.insert(bb) { + if let Some(term) = &self.mir[bb].terminator { self.visit_stack.push((bb, term.successors())); } } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 293b5c63cf06a..54d9e24bbc0e2 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1353,6 +1353,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "generate build artifacts that are compatible with linker-based LTO."), no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED], "don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"), + no_leak_check: bool = (false, parse_bool, [UNTRACKED], + "disables the 'leak check' for subtyping; unsound, but useful for tests"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index bd24b93f0293f..d23bd2e51fbd5 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1481,7 +1481,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> { /// type name in a non-zero universe is a skolemized type -- an /// idealized representative of "types in general" that we use for /// checking generic functions. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] pub struct UniverseIndex(u32); impl UniverseIndex { @@ -1489,6 +1489,19 @@ impl UniverseIndex { /// visible. pub const ROOT: Self = UniverseIndex(0); + /// The "max universe" -- this isn't really a valid universe, but + /// it's useful sometimes as a "starting value" when you are + /// taking the minimum of a (non-empty!) set of universes. + pub const MAX: Self = UniverseIndex(::std::u32::MAX); + + /// Creates a universe index from the given integer. Not to be + /// used lightly lest you pick a bad value. But sometimes we + /// convert universe indicies into integers and back for various + /// reasons. + pub fn from_u32(index: u32) -> Self { + UniverseIndex(index) + } + /// A "subuniverse" corresponds to being inside a `forall` quantifier. /// So, for example, suppose we have this type in universe `U`: /// @@ -1504,6 +1517,11 @@ impl UniverseIndex { UniverseIndex(self.0.checked_add(1).unwrap()) } + /// True if the names in this universe are a subset of the names in `other`. + pub fn is_subset_of(self, other: UniverseIndex) -> bool { + self.0 <= other.0 + } + pub fn as_u32(&self) -> u32 { self.0 } @@ -1513,6 +1531,12 @@ impl UniverseIndex { } } +impl fmt::Debug for UniverseIndex { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "U{}", self.as_u32()) + } +} + impl From for UniverseIndex { fn from(index: u32) -> Self { UniverseIndex(index) diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs index 9ced0f5f4eca1..fcde7f9bbc33d 100644 --- a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs +++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs @@ -65,7 +65,7 @@ pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebu let mut has_variables = BitVector::new(mir.source_scopes.len()); for var in mir.vars_iter() { let decl = &mir.local_decls[var]; - has_variables.insert(decl.visibility_scope.index()); + has_variables.insert(decl.visibility_scope); } // Instantiate all scopes. @@ -79,7 +79,7 @@ pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebu fn make_mir_scope(cx: &CodegenCx, mir: &Mir, - has_variables: &BitVector, + has_variables: &BitVector, debug_context: &FunctionDebugContextData, scope: SourceScope, scopes: &mut IndexVec) { @@ -102,7 +102,7 @@ fn make_mir_scope(cx: &CodegenCx, return; }; - if !has_variables.contains(scope.index()) { + if !has_variables.contains(scope) { // Do not create a DIScope if there are no variables // defined in this MIR Scope, to avoid debuginfo bloat. diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs index efd829c283f06..e105baba8aa2e 100644 --- a/src/librustc_codegen_llvm/mir/analyze.rs +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -22,7 +22,7 @@ use rustc::ty::layout::LayoutOf; use type_of::LayoutLlvmExt; use super::FunctionCx; -pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { +pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { let mir = fx.mir; let mut analyzer = LocalAnalyzer::new(fx); @@ -54,7 +54,7 @@ pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { fx: &'mir FunctionCx<'a, 'tcx>, dominators: Dominators, - non_ssa_locals: BitVector, + non_ssa_locals: BitVector, // The location of the first visited direct assignment to each // local, or an invalid location (out of bounds `block` index). first_assignment: IndexVec @@ -90,7 +90,7 @@ impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { fn not_ssa(&mut self, local: mir::Local) { debug!("marking {:?} as non-SSA", local); - self.non_ssa_locals.insert(local.index()); + self.non_ssa_locals.insert(local); } fn assign(&mut self, local: mir::Local, location: Location) { diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs index 608539dd3fa7e..312939408c62c 100644 --- a/src/librustc_codegen_llvm/mir/mod.rs +++ b/src/librustc_codegen_llvm/mir/mod.rs @@ -268,7 +268,7 @@ pub fn codegen_mir<'a, 'tcx: 'a>( let debug_scope = fx.scopes[decl.visibility_scope]; let dbg = debug_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo; - if !memory_locals.contains(local.index()) && !dbg { + if !memory_locals.contains(local) && !dbg { debug!("alloc: {:?} ({}) -> operand", local, name); return LocalRef::new_operand(bx.cx, layout); } @@ -291,7 +291,7 @@ pub fn codegen_mir<'a, 'tcx: 'a>( debug!("alloc: {:?} (return place) -> place", local); let llretptr = llvm::get_param(llfn, 0); LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align)) - } else if memory_locals.contains(local.index()) { + } else if memory_locals.contains(local) { debug!("alloc: {:?} -> place", local); LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local))) } else { @@ -415,7 +415,7 @@ fn create_funclets<'a, 'tcx>( fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, fx: &FunctionCx<'a, 'tcx>, scopes: &IndexVec, - memory_locals: &BitVector) + memory_locals: &BitVector) -> Vec> { let mir = fx.mir; let tcx = bx.tcx(); @@ -487,7 +487,7 @@ fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, llarg_idx += 1; } - if arg_scope.is_none() && !memory_locals.contains(local.index()) { + if arg_scope.is_none() && !memory_locals.contains(local) { // We don't have to cast or keep the argument in the alloca. // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead // of putting everything in allocas just so we can use llvm.dbg.declare. diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index ee903e49642fc..04d6cb5e2a6d2 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -17,16 +17,18 @@ const WORD_BITS: usize = 128; /// A very simple BitVector type. #[derive(Clone, Debug, PartialEq)] -pub struct BitVector { +pub struct BitVector { data: Vec, + marker: PhantomData, } -impl BitVector { +impl BitVector { #[inline] - pub fn new(num_bits: usize) -> BitVector { + pub fn new(num_bits: usize) -> BitVector { let num_words = words(num_bits); BitVector { data: vec![0; num_words], + marker: PhantomData, } } @@ -41,15 +43,30 @@ impl BitVector { self.data.iter().map(|e| e.count_ones() as usize).sum() } + /// True if `self` contains the bit `bit`. #[inline] - pub fn contains(&self, bit: usize) -> bool { + pub fn contains(&self, bit: C) -> bool { let (word, mask) = word_mask(bit); (self.data[word] & mask) != 0 } + /// True if `self` contains all the bits in `other`. + /// + /// The two vectors must have the same length. + #[inline] + pub fn contains_all(&self, other: &BitVector) -> bool { + assert_eq!(self.data.len(), other.data.len()); + self.data.iter().zip(&other.data).all(|(a, b)| (a & b) == *b) + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.data.iter().all(|a| *a == 0) + } + /// Returns true if the bit has changed. #[inline] - pub fn insert(&mut self, bit: usize) -> bool { + pub fn insert(&mut self, bit: C) -> bool { let (word, mask) = word_mask(bit); let data = &mut self.data[word]; let value = *data; @@ -58,9 +75,16 @@ impl BitVector { new_value != value } + /// Sets all bits to true. + pub fn insert_all(&mut self) { + for data in &mut self.data { + *data = u128::max_value(); + } + } + /// Returns true if the bit has changed. #[inline] - pub fn remove(&mut self, bit: usize) -> bool { + pub fn remove(&mut self, bit: C) -> bool { let (word, mask) = word_mask(bit); let data = &mut self.data[word]; let value = *data; @@ -70,7 +94,7 @@ impl BitVector { } #[inline] - pub fn merge(&mut self, all: &BitVector) -> bool { + pub fn merge(&mut self, all: &BitVector) -> bool { assert!(self.data.len() == all.data.len()); let mut changed = false; for (i, j) in self.data.iter_mut().zip(&all.data) { @@ -84,7 +108,7 @@ impl BitVector { } #[inline] - pub fn grow(&mut self, num_bits: usize) { + pub fn grow(&mut self, num_bits: C) { let num_words = words(num_bits); if self.data.len() < num_words { self.data.resize(num_words, 0) @@ -93,24 +117,26 @@ impl BitVector { /// Iterates over indexes of set bits in a sorted order #[inline] - pub fn iter<'a>(&'a self) -> BitVectorIter<'a> { + pub fn iter<'a>(&'a self) -> BitVectorIter<'a, C> { BitVectorIter { iter: self.data.iter(), current: 0, idx: 0, + marker: PhantomData, } } } -pub struct BitVectorIter<'a> { +pub struct BitVectorIter<'a, C: Idx> { iter: ::std::slice::Iter<'a, Word>, current: Word, idx: usize, + marker: PhantomData } -impl<'a> Iterator for BitVectorIter<'a> { - type Item = usize; - fn next(&mut self) -> Option { +impl<'a, C: Idx> Iterator for BitVectorIter<'a, C> { + type Item = C; + fn next(&mut self) -> Option { while self.current == 0 { self.current = if let Some(&i) = self.iter.next() { if i == 0 { @@ -128,7 +154,7 @@ impl<'a> Iterator for BitVectorIter<'a> { self.current >>= offset; self.current >>= 1; // shift otherwise overflows for 0b1000_0000_…_0000 self.idx += offset + 1; - return Some(self.idx - 1); + return Some(C::new(self.idx - 1)); } fn size_hint(&self) -> (usize, Option) { @@ -137,8 +163,8 @@ impl<'a> Iterator for BitVectorIter<'a> { } } -impl FromIterator for BitVector { - fn from_iter(iter: I) -> BitVector +impl FromIterator for BitVector { + fn from_iter(iter: I) -> BitVector where I: IntoIterator, { @@ -150,10 +176,10 @@ impl FromIterator for BitVector { let mut bv = BitVector::new(len); for (idx, val) in iter.enumerate() { if idx > len { - bv.grow(idx); + bv.grow(C::new(idx)); } if val { - bv.insert(idx); + bv.insert(C::new(idx)); } } @@ -165,25 +191,28 @@ impl FromIterator for BitVector { /// one gigantic bitvector. In other words, it is as if you have /// `rows` bitvectors, each of length `columns`. #[derive(Clone, Debug)] -pub struct BitMatrix { +pub struct BitMatrix { columns: usize, vector: Vec, + phantom: PhantomData<(R, C)>, } -impl BitMatrix { +impl BitMatrix { /// Create a new `rows x columns` matrix, initially empty. - pub fn new(rows: usize, columns: usize) -> BitMatrix { + pub fn new(rows: usize, columns: usize) -> BitMatrix { // For every element, we need one bit for every other // element. Round up to an even number of words. let words_per_row = words(columns); BitMatrix { columns, vector: vec![0; rows * words_per_row], + phantom: PhantomData, } } /// The range of bits for a given row. - fn range(&self, row: usize) -> (usize, usize) { + fn range(&self, row: R) -> (usize, usize) { + let row = row.index(); let words_per_row = words(self.columns); let start = row * words_per_row; (start, start + words_per_row) @@ -193,7 +222,7 @@ impl BitMatrix { /// `column` to the bitset for `row`. /// /// Returns true if this changed the matrix, and false otherwise. - pub fn add(&mut self, row: usize, column: usize) -> bool { + pub fn add(&mut self, row: R, column: R) -> bool { let (start, _) = self.range(row); let (word, mask) = word_mask(column); let vector = &mut self.vector[..]; @@ -207,7 +236,7 @@ impl BitMatrix { /// the matrix cell at `(row, column)` true? Put yet another way, /// if the matrix represents (transitive) reachability, can /// `row` reach `column`? - pub fn contains(&self, row: usize, column: usize) -> bool { + pub fn contains(&self, row: R, column: R) -> bool { let (start, _) = self.range(row); let (word, mask) = word_mask(column); (self.vector[start + word] & mask) != 0 @@ -217,7 +246,7 @@ impl BitMatrix { /// is an O(n) operation where `n` is the number of elements /// (somewhat independent from the actual size of the /// intersection, in particular). - pub fn intersection(&self, a: usize, b: usize) -> Vec { + pub fn intersection(&self, a: R, b: R) -> Vec { let (a_start, a_end) = self.range(a); let (b_start, b_end) = self.range(b); let mut result = Vec::with_capacity(self.columns); @@ -228,7 +257,7 @@ impl BitMatrix { break; } if v & 0x1 != 0 { - result.push(base * WORD_BITS + bit); + result.push(C::new(base * WORD_BITS + bit)); } v >>= 1; } @@ -243,7 +272,7 @@ impl BitMatrix { /// you have an edge `write -> read`, because in that case /// `write` can reach everything that `read` can (and /// potentially more). - pub fn merge(&mut self, read: usize, write: usize) -> bool { + pub fn merge(&mut self, read: R, write: R) -> bool { let (read_start, read_end) = self.range(read); let (write_start, write_end) = self.range(write); let vector = &mut self.vector[..]; @@ -259,12 +288,13 @@ impl BitMatrix { /// Iterates through all the columns set to true in a given row of /// the matrix. - pub fn iter<'a>(&'a self, row: usize) -> BitVectorIter<'a> { + pub fn iter<'a>(&'a self, row: R) -> BitVectorIter<'a, C> { let (start, end) = self.range(row); BitVectorIter { iter: self.vector[start..end].iter(), current: 0, idx: 0, + marker: PhantomData, } } } @@ -278,8 +308,7 @@ where C: Idx, { columns: usize, - vector: IndexVec, - marker: PhantomData, + vector: IndexVec>, } impl SparseBitMatrix { @@ -288,19 +317,22 @@ impl SparseBitMatrix { Self { columns, vector: IndexVec::new(), - marker: PhantomData, } } + fn ensure_row(&mut self, row: R) { + let columns = self.columns; + self.vector + .ensure_contains_elem(row, || BitVector::new(columns)); + } + /// Sets the cell at `(row, column)` to true. Put another way, insert /// `column` to the bitset for `row`. /// /// Returns true if this changed the matrix, and false otherwise. pub fn add(&mut self, row: R, column: C) -> bool { - let columns = self.columns; - self.vector - .ensure_contains_elem(row, || BitVector::new(columns)); - self.vector[row].insert(column.index()) + self.ensure_row(row); + self.vector[row].insert(column) } /// Do the bits from `row` contain `column`? Put another way, is @@ -308,7 +340,7 @@ impl SparseBitMatrix { /// if the matrix represents (transitive) reachability, can /// `row` reach `column`? pub fn contains(&self, row: R, column: C) -> bool { - self.vector.get(row).map_or(false, |r| r.contains(column.index())) + self.vector.get(row).map_or(false, |r| r.contains(column)) } /// Add the bits from row `read` to the bits from row `write`, @@ -323,45 +355,56 @@ impl SparseBitMatrix { return false; } - let columns = self.columns; - self.vector - .ensure_contains_elem(write, || BitVector::new(columns)); + self.ensure_row(write); let (bitvec_read, bitvec_write) = self.vector.pick2_mut(read, write); bitvec_write.merge(bitvec_read) } /// Merge a row, `from`, into the `into` row. - pub fn merge_into(&mut self, into: R, from: &BitVector) -> bool { - let columns = self.columns; - self.vector - .ensure_contains_elem(into, || BitVector::new(columns)); + pub fn merge_into(&mut self, into: R, from: &BitVector) -> bool { + self.ensure_row(into); self.vector[into].merge(from) } + /// Add all bits to the given row. + pub fn add_all(&mut self, row: R) { + self.ensure_row(row); + self.vector[row].insert_all(); + } + /// Number of elements in the matrix. pub fn len(&self) -> usize { self.vector.len() } + pub fn rows(&self) -> impl Iterator { + self.vector.indices() + } + /// Iterates through all the columns set to true in a given row of /// the matrix. pub fn iter<'a>(&'a self, row: R) -> impl Iterator + 'a { - self.vector.get(row).into_iter().flat_map(|r| r.iter().map(|n| C::new(n))) + self.vector.get(row).into_iter().flat_map(|r| r.iter()) } /// Iterates through each row and the accompanying bit set. - pub fn iter_enumerated<'a>(&'a self) -> impl Iterator + 'a { + pub fn iter_enumerated<'a>(&'a self) -> impl Iterator)> + 'a { self.vector.iter_enumerated() } + + pub fn row(&self, row: R) -> Option<&BitVector> { + self.vector.get(row) + } } #[inline] -fn words(elements: usize) -> usize { - (elements + WORD_BITS - 1) / WORD_BITS +fn words(elements: C) -> usize { + (elements.index() + WORD_BITS - 1) / WORD_BITS } #[inline] -fn word_mask(index: usize) -> (usize, Word) { +fn word_mask(index: C) -> (usize, Word) { + let index = index.index(); let word = index / WORD_BITS; let mask = 1 << (index % WORD_BITS); (word, mask) @@ -369,7 +412,7 @@ fn word_mask(index: usize) -> (usize, Word) { #[test] fn bitvec_iter_works() { - let mut bitvec = BitVector::new(100); + let mut bitvec: BitVector = BitVector::new(100); bitvec.insert(1); bitvec.insert(10); bitvec.insert(19); @@ -387,7 +430,7 @@ fn bitvec_iter_works() { #[test] fn bitvec_iter_works_2() { - let mut bitvec = BitVector::new(319); + let mut bitvec: BitVector = BitVector::new(319); bitvec.insert(0); bitvec.insert(127); bitvec.insert(191); @@ -398,8 +441,8 @@ fn bitvec_iter_works_2() { #[test] fn union_two_vecs() { - let mut vec1 = BitVector::new(65); - let mut vec2 = BitVector::new(65); + let mut vec1: BitVector = BitVector::new(65); + let mut vec2: BitVector = BitVector::new(65); assert!(vec1.insert(3)); assert!(!vec1.insert(3)); assert!(vec2.insert(5)); @@ -415,7 +458,7 @@ fn union_two_vecs() { #[test] fn grow() { - let mut vec1 = BitVector::new(65); + let mut vec1: BitVector = BitVector::new(65); for index in 0..65 { assert!(vec1.insert(index)); assert!(!vec1.insert(index)); @@ -441,7 +484,7 @@ fn grow() { #[test] fn matrix_intersection() { - let mut vec1 = BitMatrix::new(200, 200); + let mut vec1: BitMatrix = BitMatrix::new(200, 200); // (*) Elements reachable from both 2 and 65. @@ -472,7 +515,49 @@ fn matrix_intersection() { #[test] fn matrix_iter() { - let mut matrix = BitMatrix::new(64, 100); + let mut matrix: BitMatrix = BitMatrix::new(64, 100); + matrix.add(3, 22); + matrix.add(3, 75); + matrix.add(2, 99); + matrix.add(4, 0); + matrix.merge(3, 5); + + let expected = [99]; + let mut iter = expected.iter(); + for i in matrix.iter(2) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); + + let expected = [22, 75]; + let mut iter = expected.iter(); + for i in matrix.iter(3) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); + + let expected = [0]; + let mut iter = expected.iter(); + for i in matrix.iter(4) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); + + let expected = [22, 75]; + let mut iter = expected.iter(); + for i in matrix.iter(5) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); +} + +#[test] +fn sparse_matrix_iter() { + let mut matrix: SparseBitMatrix = SparseBitMatrix::new(100); matrix.add(3, 22); matrix.add(3, 75); matrix.add(2, 99); diff --git a/src/librustc_data_structures/graph/implementation/mod.rs b/src/librustc_data_structures/graph/implementation/mod.rs index e2b393071ff5c..dbfc09116bbaa 100644 --- a/src/librustc_data_structures/graph/implementation/mod.rs +++ b/src/librustc_data_structures/graph/implementation/mod.rs @@ -348,7 +348,7 @@ where { graph: &'g Graph, stack: Vec, - visited: BitVector, + visited: BitVector, direction: Direction, } diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index e7a75c149ccf0..c358f2f852e18 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -25,7 +25,13 @@ use rustc_serialize as serialize; /// (purpose: avoid mixing indexes for different bitvector domains.) pub trait Idx: Copy + 'static + Ord + Debug + Hash { fn new(idx: usize) -> Self; + fn index(self) -> usize; + + fn increment_by(&mut self, amount: usize) { + let v = self.index() + amount; + *self = Self::new(v); + } } impl Idx for usize { @@ -89,6 +95,35 @@ macro_rules! newtype_index { } } + impl ::std::iter::Step for $type { + fn steps_between(start: &Self, end: &Self) -> Option { + ::steps_between( + &Idx::index(*start), + &Idx::index(*end), + ) + } + + fn replace_one(&mut self) -> Self { + ::std::mem::replace(self, Self::new(1)) + } + + fn replace_zero(&mut self) -> Self { + ::std::mem::replace(self, Self::new(0)) + } + + fn add_one(&self) -> Self { + Self::new(Idx::index(*self) + 1) + } + + fn sub_one(&self) -> Self { + Self::new(Idx::index(*self) - 1) + } + + fn add_usize(&self, u: usize) -> Option { + Idx::index(*self).checked_add(u).map(Self::new) + } + } + newtype_index!( @handle_debug @derives [$($derives,)*] @@ -475,8 +510,8 @@ impl IndexVec { } #[inline] - pub fn swap(&mut self, a: usize, b: usize) { - self.raw.swap(a, b) + pub fn swap(&mut self, a: I, b: I) { + self.raw.swap(a.index(), b.index()) } #[inline] diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 6d63bc4436fe8..a8124fb7c5b62 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -39,7 +39,7 @@ pub struct TransitiveRelation { // are added with new elements. Perhaps better would be to ask the // user for a batch of edges to minimize this effect, but I // already wrote the code this way. :P -nmatsakis - closure: Lock>, + closure: Lock>>, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] @@ -354,7 +354,7 @@ impl TransitiveRelation { } fn with_closure(&self, op: OP) -> R - where OP: FnOnce(&BitMatrix) -> R + where OP: FnOnce(&BitMatrix) -> R { let mut closure_cell = self.closure.borrow_mut(); let mut closure = closure_cell.take(); @@ -366,7 +366,7 @@ impl TransitiveRelation { result } - fn compute_closure(&self) -> BitMatrix { + fn compute_closure(&self) -> BitMatrix { let mut matrix = BitMatrix::new(self.elements.len(), self.elements.len()); let mut changed = true; @@ -396,7 +396,7 @@ impl TransitiveRelation { /// - Input: `[a, b, x]`. Output: `[a, x]`. /// - Input: `[b, a, x]`. Output: `[b, a, x]`. /// - Input: `[a, x, b, y]`. Output: `[a, x]`. -fn pare_down(candidates: &mut Vec, closure: &BitMatrix) { +fn pare_down(candidates: &mut Vec, closure: &BitMatrix) { let mut i = 0; while i < candidates.len() { let candidate_i = candidates[i]; diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index f274f8e91894e..457499ded5657 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -12,7 +12,7 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::LocationTable; use borrow_check::nll::ToRegionVid; use borrow_check::nll::facts::AllFacts; -use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::region_infer::values::LivenessValues; use rustc::infer::InferCtxt; use rustc::mir::visit::TyContext; use rustc::mir::visit::Visitor; @@ -20,11 +20,11 @@ use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue}; use rustc::mir::{Local, Statement, Terminator}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; -use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts}; +use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts, RegionVid}; pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, - regioncx: &mut RegionInferenceContext<'tcx>, + liveness_constraints: &mut LivenessValues, all_facts: &mut Option, location_table: &LocationTable, mir: &Mir<'tcx>, @@ -33,7 +33,7 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( let mut cg = ConstraintGeneration { borrow_set, infcx, - regioncx, + liveness_constraints, location_table, all_facts, }; @@ -48,7 +48,7 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>, all_facts: &'cg mut Option, location_table: &'cg LocationTable, - regioncx: &'cg mut RegionInferenceContext<'tcx>, + liveness_constraints: &'cg mut LivenessValues, borrow_set: &'cg BorrowSet<'tcx>, } @@ -202,7 +202,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { .tcx .for_each_free_region(&live_ty, |live_region| { let vid = live_region.to_region_vid(); - self.regioncx.add_live_element(vid, location); + self.liveness_constraints.add_element(vid, location); }); } } diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index bc4646b7c789a..0039958b6b14d 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -54,7 +54,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { borrow_region_vid ); - let region_sub = regioncx.find_constraint(borrow_region_vid, context.loc); + let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc); debug!( "explain_why_borrow_contains_point: region_sub={:?}", diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index edc701bad1eb3..63d5de4f2e525 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -104,7 +104,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( None }; - let elements = &Rc::new(RegionValueElements::new(mir, universal_regions.len())); + let elements = &Rc::new(RegionValueElements::new(mir)); // Run the MIR type-checker. let liveness_map = NllLivenessMap::compute(&mir); @@ -136,10 +136,20 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // base constraints generated by the type-check. let var_origins = infcx.take_region_var_origins(); let MirTypeckRegionConstraints { - liveness_constraints, + mut liveness_constraints, outlives_constraints, type_tests, } = constraint_sets; + + constraint_generation::generate_constraints( + infcx, + &mut liveness_constraints, + &mut all_facts, + location_table, + &mir, + borrow_set, + ); + let mut regioncx = RegionInferenceContext::new( var_origins, universal_regions, @@ -151,14 +161,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( ); // Generate various additional constraints. - constraint_generation::generate_constraints( - infcx, - &mut regioncx, - &mut all_facts, - location_table, - &mir, - borrow_set, - ); invalidation::generate_invalidates( infcx, &mut all_facts, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs index 3c73203706dcb..88b34c767324c 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs @@ -13,6 +13,7 @@ //! state of region inference. This code handles emitting the region //! context internal state. +use rustc::infer::NLLRegionVariableOrigin; use std::io::{self, Write}; use super::{OutlivesConstraint, RegionInferenceContext}; @@ -27,8 +28,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { writeln!(out, "| Free Region Mapping")?; for region in self.regions() { - if self.definitions[region].is_universal { - let classification = self.universal_regions.region_classification(region).unwrap(); + if let NLLRegionVariableOrigin::FreeRegion = self.definitions[region].origin { + let classification = self + .universal_regions + .region_classification(region) + .unwrap(); let outlived_by = self.universal_regions.regions_outlived_by(region); writeln!( out, @@ -47,9 +51,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { for region in self.regions() { writeln!( out, - "| {r:rw$} | {v}", + "| {r:rw$} | {ui:4?} | {v}", r = format!("{:?}", region), rw = REGION_WIDTH, + ui = self.region_universe(region), v = self.region_value_str(region), )?; } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index 131e1defc1f9e..1b540ef74be49 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use borrow_check::nll::region_infer::values::ToElementIndex; use borrow_check::nll::region_infer::{ConstraintIndex, RegionInferenceContext}; use borrow_check::nll::type_check::Locations; use rustc::hir::def_id::DefId; @@ -16,9 +15,9 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::InferCtxt; use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind}; use rustc::ty::RegionVid; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::Diagnostic; +use std::collections::VecDeque; use std::fmt; use syntax_pos::Span; @@ -28,7 +27,7 @@ mod var_name; /// Constraints that are considered interesting can be categorized to /// determine why they are interesting. Order of variants indicates /// sort order of the category, thereby influencing diagnostic output. -#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] enum ConstraintCategory { Cast, Assignment, @@ -43,77 +42,178 @@ enum ConstraintCategory { impl fmt::Display for ConstraintCategory { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - ConstraintCategory::Assignment | - ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"), + ConstraintCategory::Assignment | ConstraintCategory::AssignmentToUpvar => { + write!(f, "assignment") + } ConstraintCategory::Return => write!(f, "return"), ConstraintCategory::Cast => write!(f, "cast"), - ConstraintCategory::CallArgument | - ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"), + ConstraintCategory::CallArgument | ConstraintCategory::CallArgumentToUpvar => { + write!(f, "argument") + } _ => write!(f, "free region"), } } } +#[derive(Copy, Clone, PartialEq, Eq)] +enum Trace { + StartRegion, + FromConstraint(ConstraintIndex), + NotVisited, +} + impl<'tcx> RegionInferenceContext<'tcx> { + /// Tries to find the best constraint to blame for the fact that + /// `R: from_region`, where `R` is some region that meets + /// `target_test`. This works by following the constraint graph, + /// creating a constraint path that forces `R` to outlive + /// `from_region`, and then finding the best choices within that + /// path to blame. + fn best_blame_constraint( + &self, + mir: &Mir<'tcx>, + from_region: RegionVid, + target_test: impl Fn(RegionVid) -> bool, + ) -> (ConstraintCategory, Span, RegionVid) { + debug!("best_blame_constraint(from_region={:?})", from_region); + + // Find all paths + let (path, target_region) = self + .find_constraint_paths_between_regions(from_region, target_test) + .unwrap(); + debug!( + "best_blame_constraint: path={:#?}", + path.iter() + .map(|&ci| format!( + "{:?}: {:?} ({:?}: {:?})", + ci, + &self.constraints[ci], + self.constraint_sccs.scc(self.constraints[ci].sup), + self.constraint_sccs.scc(self.constraints[ci].sub), + )) + .collect::>() + ); + + // Classify each of the constraints along the path. + let mut categorized_path: Vec<(ConstraintCategory, Span)> = path + .iter() + .map(|&index| self.classify_constraint(index, mir)) + .collect(); + debug!( + "best_blame_constraint: categorized_path={:#?}", + categorized_path + ); + + // To find the best span to cite, we first try to look for the + // final constraint that is interesting and where the `sup` is + // not unified with the ultimate target region. The reason + // for this is that we have a chain of constraints that lead + // from the source to the target region, something like: + // + // '0: '1 ('0 is the source) + // '1: '2 + // '2: '3 + // '3: '4 + // '4: '5 + // '5: '6 ('6 is the target) + // + // Some of those regions are unified with `'6` (in the same + // SCC). We want to screen those out. After that point, the + // "closest" constraint we have to the end is going to be the + // most likely to be the point where the value escapes -- but + // we still want to screen for an "interesting" point to + // highlight (e.g., a call site or something). + let target_scc = self.constraint_sccs.scc(target_region); + let best_choice = (0..path.len()).rev().find(|&i| { + let constraint = &self.constraints[path[i]]; + + let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup); + if constraint_sup_scc == target_scc { + return false; + } + + match categorized_path[i].0 { + ConstraintCategory::Boring => false, + _ => true, + } + }); + if let Some(i) = best_choice { + let (category, span) = categorized_path[i]; + return (category, span, target_region); + } + + // If that search fails, that is.. unusual. Maybe everything + // is in the same SCC or something. In that case, find what + // appears to be the most interesting point to report to the + // user via an even more ad-hoc guess. + categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0)); + debug!("best_blame_constraint: sorted_path={:#?}", categorized_path); + + let &(category, span) = categorized_path.first().unwrap(); + + (category, span, target_region) + } + /// Walks the graph of constraints (where `'a: 'b` is considered /// an edge `'a -> 'b`) to find all paths from `from_region` to /// `to_region`. The paths are accumulated into the vector /// `results`. The paths are stored as a series of /// `ConstraintIndex` values -- in other words, a list of *edges*. + /// + /// Returns: a series of constraints as well as the region `R` + /// that passed the target test. fn find_constraint_paths_between_regions( &self, from_region: RegionVid, target_test: impl Fn(RegionVid) -> bool, - ) -> Vec> { - let mut results = vec![]; - self.find_constraint_paths_between_regions_helper( - from_region, - from_region, - &target_test, - &mut FxHashSet::default(), - &mut vec![], - &mut results, - ); - results - } + ) -> Option<(Vec, RegionVid)> { + let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); + context[from_region] = Trace::StartRegion; + + // Use a deque so that we do a breadth-first search. We will + // stop at the first match, which ought to be the shortest + // path (fewest constraints). + let mut deque = VecDeque::new(); + deque.push_back(from_region); + + while let Some(r) = deque.pop_front() { + // Check if we reached the region we were looking for. If so, + // we can reconstruct the path that led to it and return it. + if target_test(r) { + let mut result = vec![]; + let mut p = r; + loop { + match context[p] { + Trace::NotVisited => { + bug!("found unvisited region {:?} on path to {:?}", p, r) + } + Trace::FromConstraint(c) => { + result.push(c); + p = self.constraints[c].sup; + } - /// Helper for `find_constraint_paths_between_regions`. - fn find_constraint_paths_between_regions_helper( - &self, - from_region: RegionVid, - current_region: RegionVid, - target_test: &impl Fn(RegionVid) -> bool, - visited: &mut FxHashSet, - stack: &mut Vec, - results: &mut Vec>, - ) { - // Check if we already visited this region. - if !visited.insert(current_region) { - return; - } + Trace::StartRegion => { + result.reverse(); + return Some((result, r)); + } + } + } + } - // Check if we reached the region we were looking for. - if target_test(current_region) { - if !stack.is_empty() { - assert_eq!(self.constraints[stack[0]].sup, from_region); - results.push(stack.clone()); + // Otherwise, walk over the outgoing constraints and + // enqueue any regions we find, keeping track of how we + // reached them. + for constraint in self.constraint_graph.outgoing_edges(r) { + assert_eq!(self.constraints[constraint].sup, r); + let sub_region = self.constraints[constraint].sub; + if let Trace::NotVisited = context[sub_region] { + context[sub_region] = Trace::FromConstraint(constraint); + deque.push_back(sub_region); + } } - return; } - for constraint in self.constraint_graph.outgoing_edges(current_region) { - assert_eq!(self.constraints[constraint].sup, current_region); - stack.push(constraint); - self.find_constraint_paths_between_regions_helper( - from_region, - self.constraints[constraint].sub, - target_test, - visited, - stack, - results, - ); - stack.pop(); - } + None } /// This function will return true if a constraint is interesting and false if a constraint @@ -136,19 +236,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, index: ConstraintIndex, mir: &Mir<'tcx>, - _infcx: &InferCtxt<'_, '_, 'tcx>, ) -> (ConstraintCategory, Span) { let constraint = self.constraints[index]; debug!("classify_constraint: constraint={:?}", constraint); let span = constraint.locations.span(mir); - let location = constraint.locations.from_location().unwrap_or(Location::START); + let location = constraint + .locations + .from_location() + .unwrap_or(Location::START); if !self.constraint_is_interesting(index) { return (ConstraintCategory::Boring, span); } let data = &mir[location.block]; - debug!("classify_constraint: location={:?} data={:?}", location, data); + debug!( + "classify_constraint: location={:?} data={:?}", + location, data + ); let category = if location.statement_index == data.statements.len() { if let Some(ref terminator) = data.terminator { debug!("classify_constraint: terminator.kind={:?}", terminator.kind); @@ -171,8 +276,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { } else { match rvalue { Rvalue::Cast(..) => ConstraintCategory::Cast, - Rvalue::Use(..) | - Rvalue::Aggregate(..) => ConstraintCategory::Assignment, + Rvalue::Use(..) | Rvalue::Aggregate(..) => { + ConstraintCategory::Assignment + } _ => ConstraintCategory::Other, } } @@ -199,61 +305,56 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, outlived_fr: RegionVid, - blame_span: Span, errors_buffer: &mut Vec, ) { debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); + let (category, span, _) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr); + + // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { let tables = infcx.tcx.typeck_tables_of(mir_def_id); - let nice = NiceRegionError::new_from_span(infcx.tcx, blame_span, o, f, Some(tables)); + let nice = NiceRegionError::new_from_span(infcx.tcx, span, o, f, Some(tables)); if let Some(_error_reported) = nice.try_report() { return; } } - // Find all paths - let constraint_paths = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr); - debug!("report_error: constraint_paths={:#?}", constraint_paths); - - // Find the shortest such path. - let path = constraint_paths.iter().min_by_key(|p| p.len()).unwrap(); - debug!("report_error: shortest_path={:?}", path); - - // Classify each of the constraints along the path. - let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter() - .map(|&index| self.classify_constraint(index, mir, infcx)) - .collect(); - debug!("report_error: categorized_path={:?}", categorized_path); - - // Find what appears to be the most interesting path to report to the user. - categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0)); - debug!("report_error: sorted_path={:?}", categorized_path); - - // Get a span - let (category, span) = categorized_path.first().unwrap(); - let category = match ( category, self.universal_regions.is_local_free_region(fr), self.universal_regions.is_local_free_region(outlived_fr), ) { - (ConstraintCategory::Assignment, true, false) => - &ConstraintCategory::AssignmentToUpvar, - (ConstraintCategory::CallArgument, true, false) => - &ConstraintCategory::CallArgumentToUpvar, + (ConstraintCategory::Assignment, true, false) => ConstraintCategory::AssignmentToUpvar, + (ConstraintCategory::CallArgument, true, false) => { + ConstraintCategory::CallArgumentToUpvar + } (category, _, _) => category, }; debug!("report_error: category={:?}", category); match category { - ConstraintCategory::AssignmentToUpvar | - ConstraintCategory::CallArgumentToUpvar => - self.report_closure_error( - mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer), - _ => - self.report_general_error( - mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer), + ConstraintCategory::AssignmentToUpvar | ConstraintCategory::CallArgumentToUpvar => self + .report_closure_error( + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + category, + span, + errors_buffer, + ), + _ => self.report_general_error( + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + category, + span, + errors_buffer, + ), } } @@ -264,23 +365,31 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, outlived_fr: RegionVid, - category: &ConstraintCategory, - span: &Span, + category: ConstraintCategory, + span: Span, errors_buffer: &mut Vec, ) { - let fr_name_and_span = self.get_var_name_and_span_for_region( - infcx.tcx, mir, fr); - let outlived_fr_name_and_span = self.get_var_name_and_span_for_region( - infcx.tcx, mir,outlived_fr); + let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr); + let outlived_fr_name_and_span = + self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr); if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() { return self.report_general_error( - mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer); + mir, + infcx, + mir_def_id, + fr, + outlived_fr, + category, + span, + errors_buffer, + ); } - let mut diag = infcx.tcx.sess.struct_span_err( - *span, &format!("borrowed data escapes outside of closure"), - ); + let mut diag = infcx + .tcx + .sess + .struct_span_err(span, &format!("borrowed data escapes outside of closure")); if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span { if let Some(name) = outlived_fr_name { @@ -295,10 +404,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(name) = fr_name { diag.span_label( fr_span, - format!("`{}` is a reference that is only valid in the closure body", name), + format!( + "`{}` is a reference that is only valid in the closure body", + name + ), ); - diag.span_label(*span, format!("`{}` escapes the closure body here", name)); + diag.span_label(span, format!("`{}` escapes the closure body here", name)); } } @@ -312,104 +424,50 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, outlived_fr: RegionVid, - category: &ConstraintCategory, - span: &Span, + category: ConstraintCategory, + span: Span, errors_buffer: &mut Vec, ) { let mut diag = infcx.tcx.sess.struct_span_err( - *span, &format!("unsatisfied lifetime constraints"), // FIXME + span, + &format!("unsatisfied lifetime constraints"), // FIXME ); let counter = &mut 1; - let fr_name = self.give_region_a_name( - infcx.tcx, mir, mir_def_id, fr, counter, &mut diag); - let outlived_fr_name = self.give_region_a_name( - infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag); - - diag.span_label(*span, format!( - "{} requires that `{}` must outlive `{}`", - category, fr_name, outlived_fr_name, - )); + let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, &mut diag); + let outlived_fr_name = + self.give_region_a_name(infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag); + + diag.span_label( + span, + format!( + "{} requires that `{}` must outlive `{}`", + category, fr_name, outlived_fr_name, + ), + ); diag.buffer(errors_buffer); } - // Find some constraint `X: Y` where: - // - `fr1: X` transitively - // - and `Y` is live at `elem` - crate fn find_constraint(&self, fr1: RegionVid, elem: Location) -> RegionVid { - let index = self.blame_constraint(fr1, elem); - self.constraints[index].sub + // Finds some region R such that `fr1: R` and `R` is live at + // `elem`. + crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { + // Find all paths + let (_path, r) = + self.find_constraint_paths_between_regions(fr1, |r| { + self.liveness_constraints.contains(r, elem) + }).unwrap(); + r } - /// Tries to finds a good span to blame for the fact that `fr1` - /// contains `fr2`. - pub(super) fn blame_constraint( + // Finds a good span to blame for the fact that `fr1` outlives `fr2`. + crate fn find_outlives_blame_span( &self, + mir: &Mir<'tcx>, fr1: RegionVid, - elem: impl ToElementIndex, - ) -> ConstraintIndex { - // Find everything that influenced final value of `fr`. - let influenced_fr1 = self.dependencies(fr1); - - // Try to find some outlives constraint `'X: fr2` where `'X` - // influenced `fr1`. Blame that. - // - // NB, this is a pretty bad choice most of the time. In - // particular, the connection between `'X` and `fr1` may not - // be obvious to the user -- not to mention the naive notion - // of dependencies, which doesn't account for the locations of - // contraints at all. But it will do for now. - let relevant_constraint = self.constraints - .iter_enumerated() - .filter_map(|(i, constraint)| { - if !self.liveness_constraints.contains(constraint.sub, elem) { - None - } else { - influenced_fr1[constraint.sup] - .map(|distance| (distance, i)) - } - }) - .min() // constraining fr1 with fewer hops *ought* to be more obvious - .map(|(_dist, i)| i); - - relevant_constraint.unwrap_or_else(|| { - bug!( - "could not find any constraint to blame for {:?}: {:?}", - fr1, - elem, - ); - }) - } - - /// Finds all regions whose values `'a` may depend on in some way. - /// 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(None, &self.definitions); - let mut changed = true; - result_set[r0] = Some(0); // distance 0 from `r0` - - while changed { - changed = false; - for constraint in self.constraints.iter() { - 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; - } - } - } - } - - result_set + fr2: RegionVid, + ) -> Span { + let (_, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2); + span } } 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 52f596f61c2e8..2ab72f655352f 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -9,18 +9,16 @@ // except according to those terms. use super::universal_regions::UniversalRegions; +use borrow_check::nll::constraints::graph::ConstraintGraph; use borrow_check::nll::constraints::{ ConstraintIndex, ConstraintSccIndex, ConstraintSet, OutlivesConstraint, }; -use borrow_check::nll::constraints::graph::ConstraintGraph; -use borrow_check::nll::region_infer::values::ToElementIndex; +use borrow_check::nll::region_infer::values::{RegionElement, ToElementIndex}; use borrow_check::nll::type_check::Locations; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::region_constraints::{GenericKind, VarInfos}; -use rustc::infer::InferCtxt; -use rustc::infer::NLLRegionVariableOrigin; -use rustc::infer::RegionVariableOrigin; +use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc::mir::{ ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location, Mir, @@ -39,7 +37,7 @@ mod dump_mir; mod error_reporting; mod graphviz; pub mod values; -use self::values::{RegionValueElements, RegionValues}; +use self::values::{LivenessValues, RegionValueElements, RegionValues}; use super::ToRegionVid; @@ -50,14 +48,11 @@ pub struct RegionInferenceContext<'tcx> { /// from as well as its final inferred value. definitions: IndexVec>, - /// Maps from points/universal-regions to a `RegionElementIndex`. - elements: Rc, - /// The liveness constraints added to each region. For most /// regions, these start out empty and steadily grow, though for /// each universally quantified region R they start out containing /// the entire CFG and `end(R)`. - liveness_constraints: RegionValues, + liveness_constraints: LivenessValues, /// The outlives constraints computed by the type-check. constraints: Rc, @@ -71,6 +66,11 @@ pub struct RegionInferenceContext<'tcx> { /// of each region. constraint_sccs: Rc>, + /// Contains the minimum universe of any variable within the same + /// SCC. We will ensure that no SCC contains values that are not + /// visible from this index. + scc_universes: IndexVec, + /// The final inferred values of the region variables; we compute /// one value per SCC. To get the value for any given *region*, /// you first find which scc it is a part of. @@ -85,17 +85,16 @@ pub struct RegionInferenceContext<'tcx> { } struct RegionDefinition<'tcx> { - /// Why we created this variable. Mostly these will be - /// `RegionVariableOrigin::NLL`, but some variables get created - /// elsewhere in the code with other causes (e.g., instantiation - /// late-bound-regions). - origin: RegionVariableOrigin, - - /// True if this is a universally quantified region. This means a - /// lifetime parameter that appears in the function signature (or, - /// in the case of a closure, in the closure environment, which of - /// course is also in the function signature). - is_universal: bool, + /// What kind of variable is this -- a free region? existential + /// variable? etc. (See the `NLLRegionVariableOrigin` for more + /// info.) + origin: NLLRegionVariableOrigin, + + /// Which universe is this region variable defined in? This is + /// most often `ty::UniverseIndex::ROOT`, but when we encounter + /// forall-quantifiers like `for<'a> { 'a = 'b }`, we would create + /// the variable for `'a` in a subuniverse. + universe: ty::UniverseIndex, /// If this is 'static or an early-bound region, then this is /// `Some(X)` where `X` is the name of the region. @@ -211,7 +210,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { _mir: &Mir<'tcx>, outlives_constraints: ConstraintSet, type_tests: Vec>, - liveness_constraints: RegionValues, + liveness_constraints: LivenessValues, elements: &Rc, ) -> Self { let universal_regions = Rc::new(universal_regions); @@ -219,37 +218,71 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Create a RegionDefinition for each inference variable. let definitions: IndexVec<_, _> = var_infos .into_iter() - .map(|info| RegionDefinition::new(info.origin)) + .map(|info| RegionDefinition::new(info.universe, info.origin)) .collect(); + // Compute the max universe used anywhere amongst the regions. + let max_universe = definitions + .iter() + .map(|d| d.universe) + .max() + .unwrap_or(ty::UniverseIndex::ROOT); + let constraints = Rc::new(outlives_constraints); // freeze constraints let constraint_graph = Rc::new(constraints.graph(definitions.len())); let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph)); - let mut scc_values = RegionValues::new(elements); + let mut scc_values = RegionValues::new(elements, universal_regions.len(), max_universe); - for (region, location_set) in liveness_constraints.iter_enumerated() { + for region in liveness_constraints.rows() { let scc = constraint_sccs.scc(region); - scc_values.merge_into(scc, location_set); + scc_values.merge_liveness(scc, region, &liveness_constraints); } + let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions); + let mut result = Self { definitions, - elements: elements.clone(), liveness_constraints, constraints, constraint_graph, constraint_sccs, + scc_universes, scc_values, type_tests, universal_regions, }; - result.init_universal_regions(); + result.init_free_and_bound_regions(); result } + /// Each SCC is the combination of many region variables which + /// have been equated. Therefore, we can associate a universe with + /// each SCC which is minimum of all the universes of its + /// constituent regions -- this is because whatever value the SCC + /// takes on must be a value that each of the regions within the + /// SCC could have as well. This implies that the SCC must have + /// the minimum, or narrowest, universe. + fn compute_scc_universes( + constraints_scc: &Sccs, + definitions: &IndexVec>, + ) -> IndexVec { + let num_sccs = constraints_scc.num_sccs(); + let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs); + + for (region_vid, region_definition) in definitions.iter_enumerated() { + let scc = constraints_scc.scc(region_vid); + let scc_universe = &mut scc_universes[scc]; + *scc_universe = ::std::cmp::min(*scc_universe, region_definition.universe); + } + + debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes); + + scc_universes + } + /// Initializes the region variables for each universally /// quantified region (lifetime parameter). The first N variables /// always correspond to the regions appearing in the function @@ -270,7 +303,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// and (b) any universally quantified regions that it outlives, /// which in this case is just itself. R1 (`'b`) in contrast also /// outlives `'a` and hence contains R0 and R1. - fn init_universal_regions(&mut self) { + fn init_free_and_bound_regions(&mut self) { // Update the names (if any) for (external_name, variable) in self.universal_regions.named_universal_regions() { debug!( @@ -281,25 +314,30 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.definitions[variable].external_name = Some(external_name); } - // For each universally quantified region X: - let elements = self.elements.clone(); - let universal_regions = self.universal_regions.clone(); - for variable in universal_regions.universal_regions() { - // These should be free-region variables. - assert!(match self.definitions[variable].origin { - RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true, - _ => false, - }); + for variable in self.definitions.indices() { + match self.definitions[variable].origin { + NLLRegionVariableOrigin::FreeRegion => { + // For each free, universally quantified region X: - self.definitions[variable].is_universal = true; + // Add all nodes in the CFG to liveness constraints + let variable_scc = self.constraint_sccs.scc(variable); + self.liveness_constraints.add_all_points(variable); + self.scc_values.add_all_points(variable_scc); - // Add all nodes in the CFG to liveness constraints - for point_index in elements.all_point_indices() { - self.add_live_element(variable, point_index); - } + // Add `end(X)` into the set for X. + self.add_element_to_scc_of(variable, variable); + } + + NLLRegionVariableOrigin::BoundRegion(ui) => { + // Each placeholder region X outlives its + // associated universe but nothing else. + self.add_element_to_scc_of(variable, ui); + } - // Add `end(X)` into the set for X. - self.add_live_element(variable, variable); + NLLRegionVariableOrigin::Existential => { + // For existential, regions, nothing to do. + } + } } } @@ -330,27 +368,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_values.region_value_str(scc) } - /// Indicates that the region variable `v` is live at the point `point`. - /// - /// Returns `true` if this constraint is new and `false` is the - /// constraint was already present. - pub(super) fn add_live_element( - &mut self, - v: RegionVid, - elem: impl ToElementIndex, - ) -> bool { - debug!("add_live_element({:?}, {:?})", v, elem); - - // Add to the liveness values for `v`... - if self.liveness_constraints.add_element(v, elem) { - // ...but also add to the SCC in which `v` appears. - let scc = self.constraint_sccs.scc(v); - self.scc_values.add_element(scc, elem); + /// Returns access to the value of `r` for debugging purposes. + crate fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { + let scc = self.constraint_sccs.scc(r.to_region_vid()); + self.scc_universes[scc] + } - true - } else { - false - } + /// Adds `elem` to the value of the SCC in which `v` appears. + fn add_element_to_scc_of(&mut self, v: RegionVid, elem: impl ToElementIndex) { + debug!("add_live_element({:?}, {:?})", v, elem); + let scc = self.constraint_sccs.scc(v); + self.scc_values.add_element(scc, elem); } /// Perform region inference and report errors if we see any @@ -460,8 +488,32 @@ impl<'tcx> RegionInferenceContext<'tcx> { // ...compute the value of `B`... self.propagate_constraint_sccs_if_new(scc_b, visited); - // ...and add elements from `B` into `A`. - self.scc_values.add_region(scc_a, scc_b); + // ...and add elements from `B` into `A`. One complication + // arises because of universes: If `B` contains something + // that `A` cannot name, then `A` can only contain `B` if + // it outlives static. + if self.universe_compatible(scc_b, scc_a) { + // `A` can name everything that is in `B`, so just + // merge the bits. + self.scc_values.add_region(scc_a, scc_b); + } else { + // Otherwise, the only way for `A` to outlive `B` + // is for it to outlive static. This is actually stricter + // than necessary: ideally, we'd support bounds like `for<'a: 'b`>` + // that might then allow us to approximate `'a` with `'b` and not + // `'static`. But it will have to do for now. + // + // The code here is a bit hacky: we grab the current + // value of the SCC in which `'static` appears, but + // this value may not be fully computed yet. That's ok + // though: it will contain the base liveness values, + // which include (a) the static free region element + // and (b) all the points in the CFG, so it is "good + // enough" to bring it in here for our purposes. + let fr_static = self.universal_regions.fr_static; + let scc_static = constraint_sccs.scc(fr_static); + self.scc_values.add_region(scc_a, scc_static); + } } debug!( @@ -471,6 +523,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } + /// True if all the elements in the value of `scc_b` are nameable + /// in `scc_a`. Used during constraint propagation, and only once + /// the value of `scc_b` has been computed. + fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { + let universe_a = self.scc_universes[scc_a]; + + // Quick check: if scc_b's declared universe is a subset of + // scc_a's declared univese (typically, both are ROOT), then + // it cannot contain any problematic universe elements. + if self.scc_universes[scc_b].is_subset_of(universe_a) { + return true; + } + + // Otherwise, we have to iterate over the universe elements in + // B's value, and check whether all of them are nameable + // from universe_a + self.scc_values + .subuniverses_contained_in(scc_b) + .all(|u| u.is_subset_of(universe_a)) + } + /// Once regions have been propagated, this method is used to see /// whether the "type tests" produced by typeck were satisfied; /// type tests encode type-outlives relationships like `T: @@ -793,7 +866,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // now). Therefore, the sup-region outlives the sub-region if, // for each universal region R1 in the sub-region, there // exists some region R2 in the sup-region that outlives R1. - let universal_outlives = self.scc_values + let universal_outlives = self + .scc_values .universal_regions_outlived_by(sub_region_scc) .all(|r1| { self.scc_values @@ -813,8 +887,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { return true; } - self.scc_values - .contains_points(sup_region_scc, sub_region_scc) + self.scc_values.contains_points(sup_region_scc, sub_region_scc) } /// Once regions have been propagated, this method is used to see @@ -842,24 +915,30 @@ impl<'tcx> RegionInferenceContext<'tcx> { mut propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut Vec, ) { - // The universal regions are always found in a prefix of the - // full list. - let universal_definitions = self.definitions - .iter_enumerated() - .take_while(|(_, fr_definition)| fr_definition.is_universal); - - // 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. - for (fr, _) in universal_definitions { - self.check_universal_region( - infcx, - mir, - mir_def_id, - fr, - &mut propagated_outlives_requirements, - errors_buffer, - ); + for (fr, fr_definition) in self.definitions.iter_enumerated() { + match fr_definition.origin { + NLLRegionVariableOrigin::FreeRegion => { + // 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. + self.check_universal_region( + infcx, + mir, + mir_def_id, + fr, + &mut propagated_outlives_requirements, + errors_buffer, + ); + } + + NLLRegionVariableOrigin::BoundRegion(universe) => { + self.check_bound_universal_region(infcx, mir, mir_def_id, fr, universe); + } + + NLLRegionVariableOrigin::Existential => { + // nothing to check here + } + } } } @@ -884,6 +963,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); + // Because this free region must be in the ROOT universe, we + // know it cannot contain any bound universes. + assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT); + debug_assert!( + self.scc_values + .subuniverses_contained_in(longer_fr_scc) + .next() + .is_none() + ); + // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { @@ -897,8 +986,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { longer_fr, shorter_fr, ); - let blame_index = self.blame_constraint(longer_fr, shorter_fr); - let blame_span = self.constraints[blame_index].locations.span(mir); + let blame_span = self.find_outlives_blame_span(mir, longer_fr, shorter_fr); if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { // Shrink `fr` until we find a non-local region (if we do). @@ -934,19 +1022,83 @@ impl<'tcx> RegionInferenceContext<'tcx> { // to report the error. This gives better error messages // in some cases. self.report_error( - mir, infcx, mir_def_id, longer_fr, shorter_fr, blame_span, errors_buffer); + mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer); } } + + fn check_bound_universal_region<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + _mir_def_id: DefId, + longer_fr: RegionVid, + universe: ty::UniverseIndex, + ) { + debug!("check_bound_universal_region(fr={:?})", longer_fr); + + let longer_fr_scc = self.constraint_sccs.scc(longer_fr); + + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + let error_element = match { + self.scc_values + .elements_contained_in(longer_fr_scc) + .find(|element| match element { + RegionElement::Location(_) => true, + RegionElement::RootUniversalRegion(_) => true, + RegionElement::SubUniversalRegion(ui) => *ui != universe, + }) + } { + Some(v) => v, + None => return, + }; + + // Find the region that introduced this `error_element`. + let error_region = match error_element { + RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), + RegionElement::RootUniversalRegion(r) => r, + RegionElement::SubUniversalRegion(error_ui) => self + .definitions + .iter_enumerated() + .filter_map(|(r, definition)| match definition.origin { + NLLRegionVariableOrigin::BoundRegion(ui) if error_ui == ui => Some(r), + _ => None, + }) + .next() + .unwrap(), + }; + + // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. + let span = self.find_outlives_blame_span(mir, longer_fr, error_region); + + // Obviously, this error message is far from satisfactory. + // At present, though, it only appears in unit tests -- + // the AST-based checker uses a more conservative check, + // so to even see this error, one must pass in a special + // flag. + let mut diag = infcx + .tcx + .sess + .struct_span_err(span, &format!("higher-ranked subtype error")); + diag.emit(); + } } impl<'tcx> RegionDefinition<'tcx> { - fn new(origin: RegionVariableOrigin) -> Self { + fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self { // Create a new region definition. Note that, for free - // regions, these fields get updated later in + // regions, the `external_name` field gets updated later in // `init_universal_regions`. + + let origin = match rv_origin { + RegionVariableOrigin::NLL(origin) => origin, + _ => NLLRegionVariableOrigin::Existential, + }; + Self { origin, - is_universal: false, + universe, external_name: None, } } 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 20b188424f9f3..8db5809e53f5e 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -9,24 +9,22 @@ // except according to those terms. use rustc::mir::{BasicBlock, Location, Mir}; -use rustc::ty::RegionVid; -use rustc_data_structures::bitvec::{BitVector, SparseBitMatrix}; +use rustc::ty::{self, RegionVid}; +use rustc_data_structures::bitvec::SparseBitMatrix; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt::Debug; use std::rc::Rc; -/// Maps between the various kinds of elements of a region value to -/// the internal indices that w use. +/// Maps between a `Location` and a `PointIndex` (and vice versa). crate struct RegionValueElements { /// For each basic block, how many points are contained within? statements_before_block: IndexVec, num_points: usize, - num_universal_regions: usize, } impl RegionValueElements { - crate fn new(mir: &Mir<'_>, num_universal_regions: usize) -> Self { + crate fn new(mir: &Mir<'_>) -> Self { let mut num_points = 0; let statements_before_block = mir .basic_blocks() @@ -37,11 +35,6 @@ impl RegionValueElements { v }) .collect(); - - debug!( - "RegionValueElements(num_universal_regions={:?})", - num_universal_regions - ); debug!( "RegionValueElements: statements_before_block={:#?}", statements_before_block @@ -50,90 +43,66 @@ impl RegionValueElements { Self { statements_before_block, - num_universal_regions, num_points, } } - /// Total number of element indices that exist. - crate fn num_elements(&self) -> usize { - self.num_points + self.num_universal_regions - } - - /// Converts an element of a region value into a `RegionElementIndex`. - crate fn index(&self, elem: T) -> RegionElementIndex { - elem.to_element_index(self) - } - - /// Iterates over the `RegionElementIndex` for all points in the CFG. - crate fn all_point_indices<'a>(&'a self) -> impl Iterator + 'a { - (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions)) - } - - /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents. - crate fn to_element(&self, i: RegionElementIndex) -> RegionElement { - debug!("to_element(i={:?})", i); - - if let Some(r) = self.to_universal_region(i) { - RegionElement::UniversalRegion(r) - } else { - let point_index = i.index() - self.num_universal_regions; - - // Find the basic block. We have a vector with the - // starting index of the statement in each block. Imagine - // we have statement #22, and we have a vector like: - // - // [0, 10, 20] - // - // In that case, this represents point_index 2 of - // basic block BB2. We know this because BB0 accounts for - // 0..10, BB1 accounts for 11..20, and BB2 accounts for - // 20... - // - // To compute this, we could do a binary search, but - // because I am lazy we instead iterate through to find - // the last point where the "first index" (0, 10, or 20) - // was less than the statement index (22). In our case, this will - // be (BB2, 20). - // - // Nit: we could do a binary search here but I'm too lazy. - let (block, &first_index) = self - .statements_before_block - .iter_enumerated() - .filter(|(_, first_index)| **first_index <= point_index) - .last() - .unwrap(); - - RegionElement::Location(Location { - block, - statement_index: point_index - first_index, - }) - } + /// Converts a `Location` into a `PointIndex`. O(1). + fn point_from_location(&self, location: Location) -> PointIndex { + let Location { + block, + statement_index, + } = location; + let start_index = self.statements_before_block[block]; + PointIndex::new(start_index + statement_index) } - /// Converts a particular `RegionElementIndex` to a universal - /// region, if that is what it represents. Returns `None` - /// otherwise. - crate fn to_universal_region(&self, i: RegionElementIndex) -> Option { - if i.index() < self.num_universal_regions { - Some(RegionVid::new(i.index())) - } else { - None + /// Converts a `PointIndex` back to a location. O(N) where N is + /// the number of blocks; could be faster if we ever cared. + crate fn to_location(&self, i: PointIndex) -> Location { + let point_index = i.index(); + + // Find the basic block. We have a vector with the + // starting index of the statement in each block. Imagine + // we have statement #22, and we have a vector like: + // + // [0, 10, 20] + // + // In that case, this represents point_index 2 of + // basic block BB2. We know this because BB0 accounts for + // 0..10, BB1 accounts for 11..20, and BB2 accounts for + // 20... + // + // To compute this, we could do a binary search, but + // because I am lazy we instead iterate through to find + // the last point where the "first index" (0, 10, or 20) + // was less than the statement index (22). In our case, this will + // be (BB2, 20). + // + // Nit: we could do a binary search here but I'm too lazy. + let (block, &first_index) = self + .statements_before_block + .iter_enumerated() + .filter(|(_, first_index)| **first_index <= point_index) + .last() + .unwrap(); + + Location { + block, + statement_index: point_index - first_index, } } } -/// A newtype for the integers that represent one of the possible -/// elements in a region. These are the rows in the `SparseBitMatrix` that -/// is used to store the values of all regions. They have the following -/// convention: -/// -/// - The first N indices represent free regions (where N = universal_regions.len()). -/// - The remainder represent the points in the CFG (see `point_indices` map). -/// -/// You can convert a `RegionElementIndex` into a `RegionElement` -/// using the `to_region_elem` method. -newtype_index!(RegionElementIndex { DEBUG_FORMAT = "RegionElementIndex({})" }); +/// A single integer representing a `Location` in the MIR control-flow +/// graph. Constructed efficiently from `RegionValueElements`. +newtype_index!(PointIndex { DEBUG_FORMAT = "PointIndex({})" }); + +/// A single integer representing a (non-zero) `UniverseIndex`. +/// Computed just by subtracting one from `UniverseIndex`; this is +/// because the `0` value for `UniverseIndex` represents the root +/// universe, and we don't need/want a bit for that one. +newtype_index!(PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" }); /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. @@ -142,113 +111,173 @@ crate enum RegionElement { /// A point in the control-flow graph. Location(Location), - /// An in-scope, universally quantified region (e.g., a lifetime parameter). - UniversalRegion(RegionVid), + /// A universally quantified region from the root universe (e.g., + /// a lifetime parameter). + RootUniversalRegion(RegionVid), + + /// A subuniverse from a subuniverse (e.g., instantiated from a + /// `for<'a> fn(&'a u32)` type). + SubUniversalRegion(ty::UniverseIndex), } -crate trait ToElementIndex: Debug + Copy { - fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex; +/// When we initially compute liveness, we use a bit matrix storing +/// points for each region-vid. +crate struct LivenessValues { + elements: Rc, + points: SparseBitMatrix, } -impl ToElementIndex for Location { - fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex { - let Location { - block, - statement_index, - } = self; - let start_index = elements.statements_before_block[block]; - RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index) +impl LivenessValues { + /// Creates a new set of "region values" that tracks causal information. + /// Each of the regions in num_region_variables will be initialized with an + /// empty set of points and no causal information. + crate fn new(elements: &Rc) -> Self { + Self { + elements: elements.clone(), + points: SparseBitMatrix::new(elements.num_points), + } } -} -impl ToElementIndex for RegionVid { - fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex { - assert!(self.index() < elements.num_universal_regions); - RegionElementIndex::new(self.index()) + /// Iterate through each region that has a value in this set. + crate fn rows<'a>(&'a self) -> impl Iterator { + self.points.rows() } -} -impl ToElementIndex for RegionElementIndex { - fn to_element_index(self, _elements: &RegionValueElements) -> RegionElementIndex { - self + /// Adds the given element to the value for the given region. Returns true if + /// the element is newly added (i.e., was not already present). + crate fn add_element(&mut self, row: N, location: Location) -> bool { + debug!("LivenessValues::add(r={:?}, location={:?})", row, location); + let index = self.elements.point_from_location(location); + self.points.add(row, index) + } + + /// Adds all the control-flow points to the values for `r`. + crate fn add_all_points(&mut self, row: N) { + self.points.add_all(row); + } + + /// True if the region `r` contains the given element. + crate fn contains(&self, row: N, location: Location) -> bool { + let index = self.elements.point_from_location(location); + self.points.contains(row, index) + } + + /// Returns a "pretty" string value of the region. Meant for debugging. + crate fn region_value_str(&self, r: N) -> String { + region_value_str( + self.points + .row(r) + .into_iter() + .flat_map(|set| set.iter()) + .map(|p| self.elements.to_location(p)) + .map(RegionElement::Location), + ) } } -/// Stores the values for a set of regions. These are stored in a -/// compact `SparseBitMatrix` representation, with one row per region -/// variable. The columns consist of either universal regions or -/// points in the CFG. +/// Stores the full values for a set of regions (in contrast to +/// `LivenessValues`, which only stores those points in the where a +/// region is live). The full value for a region may contain points in +/// the CFG, but also free regions as well as bound universe +/// placeholders. +/// +/// Example: +/// +/// ```text +/// fn foo(x: &'a u32) -> &'a u32 { +/// let y: &'0 u32 = x; // let's call this `'0` +/// y +/// } +/// ``` +/// +/// Here, the variable `'0` would contain the free region `'a`, +/// because (since it is returned) it must live for at least `'a`. But +/// it would also contain various points from within the function. #[derive(Clone)] crate struct RegionValues { elements: Rc, - matrix: SparseBitMatrix, + points: SparseBitMatrix, + free_regions: SparseBitMatrix, + + /// Placeholders represent bound regions -- so something like `'a` + /// in for<'a> fn(&'a u32)`. + placeholders: SparseBitMatrix, } impl RegionValues { /// Creates a new set of "region values" that tracks causal information. /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. - crate fn new(elements: &Rc) -> Self { + crate fn new( + elements: &Rc, + num_universal_regions: usize, + max_universe: ty::UniverseIndex, + ) -> Self { + let num_placeholders = max_universe.as_usize(); Self { elements: elements.clone(), - matrix: SparseBitMatrix::new(elements.num_elements()), + points: SparseBitMatrix::new(elements.num_points), + free_regions: SparseBitMatrix::new(num_universal_regions), + placeholders: SparseBitMatrix::new(num_placeholders), } } /// Adds the given element to the value for the given region. Returns true if /// the element is newly added (i.e., was not already present). - crate fn add_element( - &mut self, - r: N, - elem: impl ToElementIndex, - ) -> bool { - let i = self.elements.index(elem); + crate fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool { debug!("add(r={:?}, elem={:?})", r, elem); - self.matrix.add(r, i) + elem.add_to_row(self, r) + } + + /// Adds all the control-flow points to the values for `r`. + crate fn add_all_points(&mut self, r: N) { + self.points.add_all(r); } /// Add all elements in `r_from` to `r_to` (because e.g. `r_to: /// r_from`). crate fn add_region(&mut self, r_to: N, r_from: N) -> bool { - self.matrix.merge(r_from, r_to) + self.points.merge(r_from, r_to) + | self.free_regions.merge(r_from, r_to) + | self.placeholders.merge(r_from, r_to) } /// True if the region `r` contains the given element. crate fn contains(&self, r: N, elem: impl ToElementIndex) -> bool { - let i = self.elements.index(elem); - self.matrix.contains(r, i) + elem.contained_in_row(self, r) } - /// Iterates through each row and the accompanying bit set. - pub fn iter_enumerated<'a>( - &'a self - ) -> impl Iterator + 'a { - self.matrix.iter_enumerated() - } - - /// Merge a row, `from`, originating in another `RegionValues` into the `into` row. - pub fn merge_into(&mut self, into: N, from: &BitVector) -> bool { - self.matrix.merge_into(into, from) + /// `self[to] |= values[from]`, essentially: that is, take all the + /// elements for the region `from` from `values` and add them to + /// the region `to` in `self`. + crate fn merge_liveness(&mut self, to: N, from: M, values: &LivenessValues) { + if let Some(set) = values.points.row(from) { + self.points.merge_into(to, set); + } } /// True if `sup_region` contains all the CFG points that /// `sub_region` contains. Ignores universal regions. crate fn contains_points(&self, sup_region: N, sub_region: N) -> bool { - // This could be done faster by comparing the bitsets. But I - // am lazy. - self.element_indices_contained_in(sub_region) - .skip_while(|&i| self.elements.to_universal_region(i).is_some()) - .all(|e| self.contains(sup_region, e)) + if let Some(sub_row) = self.points.row(sub_region) { + if let Some(sup_row) = self.points.row(sup_region) { + sup_row.contains_all(sub_row) + } else { + // sup row is empty, so sub row must be empty + sub_row.is_empty() + } + } else { + // sub row is empty, always true + true + } } - /// Iterate over the value of the region `r`, yielding up element - /// indices. You may prefer `universal_regions_outlived_by` or - /// `elements_contained_in`. - crate fn element_indices_contained_in<'a>( - &'a self, - r: N, - ) -> impl Iterator + 'a { - self.matrix.iter(r).map(move |i| i) + /// Returns the locations contained within a given region `r`. + crate fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator + 'a { + self.points + .row(r) + .into_iter() + .flat_map(move |set| set.iter().map(move |p| self.elements.to_location(p))) } /// Returns just the universal regions that are contained in a given region's value. @@ -256,79 +285,154 @@ impl RegionValues { &'a self, r: N, ) -> impl Iterator + 'a { - self.element_indices_contained_in(r) - .map(move |i| self.elements.to_universal_region(i)) - .take_while(move |v| v.is_some()) // universal regions are a prefix - .map(move |v| v.unwrap()) + self.free_regions + .row(r) + .into_iter() + .flat_map(|set| set.iter()) } /// Returns all the elements contained in a given region's value. - crate fn elements_contained_in<'a>( + crate fn subuniverses_contained_in<'a>( &'a self, r: N, - ) -> impl Iterator + 'a { - self.element_indices_contained_in(r) - .map(move |r| self.elements.to_element(r)) + ) -> impl Iterator + 'a { + self.placeholders + .row(r) + .into_iter() + .flat_map(|set| set.iter()) + .map(|p| ty::UniverseIndex::from_u32((p.index() + 1) as u32)) + } + + /// Returns all the elements contained in a given region's value. + crate fn elements_contained_in<'a>(&'a self, r: N) -> impl Iterator + 'a { + let points_iter = self.locations_outlived_by(r).map(RegionElement::Location); + + let free_regions_iter = self + .universal_regions_outlived_by(r) + .map(RegionElement::RootUniversalRegion); + + let subuniverses_iter = self + .subuniverses_contained_in(r) + .map(RegionElement::SubUniversalRegion); + + points_iter + .chain(free_regions_iter) + .chain(subuniverses_iter) } /// Returns a "pretty" string value of the region. Meant for debugging. crate fn region_value_str(&self, r: N) -> String { - let mut result = String::new(); - 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) => { - 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); + region_value_str(self.elements_contained_in(r)) + } +} + +crate trait ToElementIndex: Debug + Copy { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool; + + fn contained_in_row(self, values: &RegionValues, row: N) -> bool; +} + +impl ToElementIndex for Location { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { + let index = values.elements.point_from_location(self); + values.points.add(row, index) + } + + fn contained_in_row(self, values: &RegionValues, row: N) -> bool { + let index = values.elements.point_from_location(self); + values.points.contains(row, index) + } +} + +impl ToElementIndex for RegionVid { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { + values.free_regions.add(row, self) + } + + fn contained_in_row(self, values: &RegionValues, row: N) -> bool { + values.free_regions.contains(row, self) + } +} + +impl ToElementIndex for ty::UniverseIndex { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { + let index = PlaceholderIndex::new(self.as_usize() - 1); + values.placeholders.add(row, index) + } + + fn contained_in_row(self, values: &RegionValues, row: N) -> bool { + let index = PlaceholderIndex::new(self.as_usize() - 1); + values.placeholders.contains(row, index) + } +} + +fn region_value_str(elements: impl IntoIterator) -> String { + let mut result = String::new(); + 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 elements { + match element { + RegionElement::Location(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; } - open_location = Some((l, l)); + push_sep(&mut result); + push_location_range(&mut result, location1, location2); } - 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; - } + open_location = Some((l, l)); + } + RegionElement::RootUniversalRegion(fr) => { + if let Some((location1, location2)) = open_location { push_sep(&mut result); - result.push_str(&format!("{:?}", fr)); + 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); - } + RegionElement::SubUniversalRegion(ur) => { + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + push_location_range(&mut result, location1, location2); + open_location = None; + } - result.push_str("}"); + push_sep(&mut result); + result.push_str(&format!("{:?}", ur)); + } + } + } - result + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + push_location_range(&mut result, location1, location2); } + result.push_str("}"); + + return result; + fn push_location_range(str: &mut String, location1: Location, location2: Location) { if location1 == location2 { str.push_str(&format!("{:?}", location1)); diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 7edee42b78ff7..e1bd8530629d9 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -28,7 +28,6 @@ pub fn renumber_mir<'tcx>(infcx: &InferCtxt<'_, '_, 'tcx>, mir: &mut Mir<'tcx>) /// variables. pub fn renumber_regions<'tcx, T>( infcx: &InferCtxt<'_, '_, 'tcx>, - ty_context: TyContext, value: &T, ) -> T where @@ -39,7 +38,7 @@ where infcx .tcx .fold_regions(value, &mut false, |_region, _depth| { - let origin = NLLRegionVariableOrigin::Inferred(ty_context); + let origin = NLLRegionVariableOrigin::Existential; infcx.next_nll_region_var(origin) }) } @@ -49,11 +48,11 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - fn renumber_regions(&mut self, ty_context: TyContext, value: &T) -> T + fn renumber_regions(&mut self, value: &T) -> T where T: TypeFoldable<'tcx>, { - renumber_regions(self.infcx, ty_context, value) + renumber_regions(self.infcx, value) } } @@ -61,7 +60,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context); - *ty = self.renumber_regions(ty_context, ty); + *ty = self.renumber_regions(ty); debug!("visit_ty: ty={:?}", ty); } @@ -69,8 +68,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { debug!("visit_substs(substs={:?}, location={:?})", substs, location); - let ty_context = TyContext::Location(location); - *substs = self.renumber_regions(ty_context, &{ *substs }); + *substs = self.renumber_regions(&{ *substs }); debug!("visit_substs: substs={:?}", substs); } @@ -79,15 +77,13 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { debug!("visit_region(region={:?}, location={:?})", region, location); let old_region = *region; - let ty_context = TyContext::Location(location); - *region = self.renumber_regions(ty_context, &old_region); + *region = self.renumber_regions(&old_region); debug!("visit_region: region={:?}", region); } - fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, location: Location) { - let ty_context = TyContext::Location(location); - *constant = self.renumber_regions(ty_context, &*constant); + fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) { + *constant = self.renumber_regions(&*constant); } fn visit_generator_substs(&mut self, @@ -99,8 +95,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { location, ); - let ty_context = TyContext::Location(location); - *substs = self.renumber_regions(ty_context, substs); + *substs = self.renumber_regions(substs); debug!("visit_generator_substs: substs={:?}", substs); } @@ -112,8 +107,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { location ); - let ty_context = TyContext::Location(location); - *substs = self.renumber_regions(ty_context, substs); + *substs = self.renumber_regions(substs); debug!("visit_closure_substs: substs={:?}", substs); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index 770a0614811dc..3d831bce5ce17 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -21,7 +21,6 @@ use borrow_check::nll::renumber; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferOk; -use rustc::mir::visit::TyContext; use rustc::mir::*; use rustc::traits::query::type_op::custom::CustomTypeOp; use rustc::traits::{ObligationCause, PredicateObligations}; @@ -117,7 +116,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); let anon_defn_ty = renumber::renumber_regions( infcx, - TyContext::Location(Location::START), &anon_defn_ty, ); debug!( 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 b6c8ffcf88d65..b67de34593f80 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -15,8 +15,8 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::LocationTable; use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; +use borrow_check::nll::region_infer::values::{RegionValueElements, LivenessValues}; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; -use borrow_check::nll::region_infer::values::{RegionValues, RegionValueElements}; use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::ToRegionVid; use borrow_check::nll::LocalWithRegion; @@ -35,7 +35,7 @@ use rustc::mir::*; use rustc::traits::query::type_op; use rustc::traits::query::{Fallible, NoSolution}; use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants, RegionVid}; +use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use std::fmt; use std::rc::Rc; use syntax_pos::{Span, DUMMY_SP}; @@ -73,6 +73,7 @@ macro_rules! span_mirbug_and_err { mod constraint_conversion; mod input_output; mod liveness; +mod relate_tys; /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to @@ -121,7 +122,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( ) -> MirTypeckRegionConstraints<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { - liveness_constraints: RegionValues::new(elements), + liveness_constraints: LivenessValues::new(elements), outlives_constraints: ConstraintSet::default(), type_tests: Vec::default(), }; @@ -638,7 +639,7 @@ crate struct MirTypeckRegionConstraints<'tcx> { /// not otherwise appear in the MIR -- in particular, the /// late-bound regions that it instantiates at call-sites -- and /// hence it must report on their liveness constraints. - crate liveness_constraints: RegionValues, + crate liveness_constraints: LivenessValues, crate outlives_constraints: ConstraintSet, @@ -796,16 +797,38 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, locations: Locations) -> Fallible<()> { - let param_env = self.param_env; - self.fully_perform_op( + relate_tys::sub_types( + self.infcx, + sub, + sup, locations, - param_env.and(type_op::subtype::Subtype::new(sub, sup)), + self.borrowck_context.as_mut().map(|x| &mut **x), ) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> { - let param_env = self.param_env; - self.fully_perform_op(locations, param_env.and(type_op::eq::Eq::new(b, a))) + relate_tys::eq_types( + self.infcx, + a, + b, + locations, + self.borrowck_context.as_mut().map(|x| &mut **x), + ) + } + + fn eq_canonical_type_and_type( + &mut self, + a: CanonicalTy<'tcx>, + b: Ty<'tcx>, + locations: Locations, + ) -> Fallible<()> { + relate_tys::eq_canonical_type_and_type( + self.infcx, + a, + b, + locations, + self.borrowck_context.as_mut().map(|x| &mut **x), + ) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { @@ -877,20 +900,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); }; } - StatementKind::UserAssertTy(ref c_ty, ref local) => { - let local_ty = mir.local_decls()[*local].ty; - let (ty, _) = self.infcx - .instantiate_canonical_with_fresh_inference_vars(stmt.source_info.span, c_ty); - debug!( - "check_stmt: user_assert_ty ty={:?} local_ty={:?}", - ty, local_ty - ); - if let Err(terr) = self.eq_types(ty, local_ty, Locations::All) { + StatementKind::UserAssertTy(c_ty, local) => { + let local_ty = mir.local_decls()[local].ty; + if let Err(terr) = self.eq_canonical_type_and_type(c_ty, local_ty, Locations::All) { span_mirbug!( self, stmt, "bad type assert ({:?} = {:?}): {:?}", - ty, + c_ty, local_ty, terr ); diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs new file mode 100644 index 0000000000000..ac9bf65b61b0f --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -0,0 +1,512 @@ +// 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 borrow_check::nll::constraints::OutlivesConstraint; +use borrow_check::nll::type_check::{BorrowCheckContext, Locations}; +use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::ToRegionVid; +use rustc::infer::canonical::{Canonical, CanonicalVarInfos}; +use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc::traits::query::Fallible; +use rustc::ty::fold::{TypeFoldable, TypeVisitor}; +use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc::ty::subst::Kind; +use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use std::mem; + +pub(super) fn sub_types<'tcx>( + infcx: &InferCtxt<'_, '_, 'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + locations: Locations, + borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>, +) -> Fallible<()> { + debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations); + TypeRelating::new( + infcx, + ty::Variance::Covariant, + locations, + borrowck_context, + ty::Slice::empty(), + ).relate(&a, &b)?; + Ok(()) +} + +pub(super) fn eq_types<'tcx>( + infcx: &InferCtxt<'_, '_, 'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + locations: Locations, + borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>, +) -> Fallible<()> { + debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations); + TypeRelating::new( + infcx, + ty::Variance::Invariant, + locations, + borrowck_context, + ty::Slice::empty(), + ).relate(&a, &b)?; + Ok(()) +} + +pub(super) fn eq_canonical_type_and_type<'tcx>( + infcx: &InferCtxt<'_, '_, 'tcx>, + a: CanonicalTy<'tcx>, + b: Ty<'tcx>, + locations: Locations, + borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>, +) -> Fallible<()> { + debug!( + "eq_canonical_type_and_type(a={:?}, b={:?}, locations={:?})", + a, b, locations + ); + let Canonical { + variables: a_variables, + value: a_value, + } = a; + TypeRelating::new( + infcx, + ty::Variance::Invariant, + locations, + borrowck_context, + a_variables, + ).relate(&a_value, &b)?; + Ok(()) +} + +struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + + /// How are we relating `a` and `b`? + /// + /// - covariant means `a <: b` + /// - contravariant means `b <: a` + /// - invariant means `a == b + /// - bivariant means that it doesn't matter + ambient_variance: ty::Variance, + + /// When we pass through a set of binders (e.g., when looking into + /// a `fn` type), we push a new bound region scope onto here. This + /// will contain the instantiated region for each region in those + /// binders. When we then encounter a `ReLateBound(d, br)`, we can + /// use the debruijn index `d` to find the right scope, and then + /// bound region name `br` to find the specific instantiation from + /// within that scope. See `replace_bound_region`. + /// + /// This field stores the instantiations for late-bound regions in + /// the `a` type. + a_scopes: Vec, + + /// Same as `a_scopes`, but for the `b` type. + b_scopes: Vec, + + /// Where (and why) is this relation taking place? + locations: Locations, + + /// This will be `Some` when we are running the type check as part + /// of NLL, and `None` if we are running a "sanity check". + borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>, + + /// As we execute, the type on the LHS *may* come from a canonical + /// source. In that case, we will sometimes find a constraint like + /// `?0 = B`, where `B` is a type from the RHS. The first time we + /// find that, we simply record `B` (and the list of scopes that + /// tells us how to *interpret* `B`). The next time we encounter + /// `?0`, then, we can read this value out and use it. + /// + /// One problem: these variables may be in some other universe, + /// how can we enforce that? I guess I could add some kind of + /// "minimum universe constraint" that we can feed to the NLL checker. + /// --> also, we know this doesn't happen + canonical_var_values: IndexVec>>, +} + +#[derive(Clone, Debug)] +struct ScopesAndKind<'tcx> { + scopes: Vec, + kind: Kind<'tcx>, +} + +#[derive(Clone, Debug, Default)] +struct BoundRegionScope { + map: FxHashMap, +} + +#[derive(Copy, Clone)] +struct UniversallyQuantified(bool); + +impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { + fn new( + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + ambient_variance: ty::Variance, + locations: Locations, + borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>, + canonical_var_infos: CanonicalVarInfos<'tcx>, + ) -> Self { + let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len()); + Self { + infcx, + ambient_variance, + borrowck_context, + locations, + canonical_var_values, + a_scopes: vec![], + b_scopes: vec![], + } + } + + fn ambient_covariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Covariant | ty::Variance::Invariant => true, + ty::Variance::Contravariant | ty::Variance::Bivariant => false, + } + } + + fn ambient_contravariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Contravariant | ty::Variance::Invariant => true, + ty::Variance::Covariant | ty::Variance::Bivariant => false, + } + } + + fn create_scope( + &mut self, + value: &ty::Binder>, + universally_quantified: UniversallyQuantified, + ) -> BoundRegionScope { + let mut scope = BoundRegionScope::default(); + value.skip_binder().visit_with(&mut ScopeInstantiator { + infcx: self.infcx, + target_index: ty::INNERMOST, + universally_quantified, + bound_region_scope: &mut scope, + }); + scope + } + + fn replace_bound_region( + &self, + universal_regions: &UniversalRegions<'tcx>, + r: ty::Region<'tcx>, + scopes: &[BoundRegionScope], + ) -> RegionVid { + match r { + ty::ReLateBound(debruijn, br) => { + // The debruijn index is a "reverse index" into the + // scopes listing. So when we have INNERMOST (0), we + // want the *last* scope pushed, and so forth. + let debruijn_index = debruijn.index() - ty::INNERMOST.index(); + let scope = &scopes[scopes.len() - debruijn_index - 1]; + + // Find this bound region in that scope to map to a + // particular region. + scope.map[br] + } + + ty::ReVar(v) => *v, + + _ => universal_regions.to_region_vid(r), + } + } + + fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) { + debug!("push_outlives({:?}: {:?})", sup, sub); + + if let Some(borrowck_context) = &mut self.borrowck_context { + borrowck_context + .constraints + .outlives_constraints + .push(OutlivesConstraint { + sup, + sub, + locations: self.locations, + }); + + // FIXME all facts! + } + } + + fn equate_var( + &mut self, + var: CanonicalVar, + b_kind: Kind<'tcx>, + ) -> RelateResult<'tcx, Kind<'tcx>> { + debug!("equate_var(var={:?}, b_kind={:?})", var, b_kind); + + // We only encounter canonical variables when equating. + assert_eq!(self.ambient_variance, ty::Variance::Invariant); + + // The canonical variable already had a value. Equate that + // value with `b`. + let old_value = self.canonical_var_values[var].clone(); + if let Some(ScopesAndKind { scopes, kind }) = old_value { + debug!("equate_var: installing kind={:?} scopes={:?}", kind, scopes); + let old_a_scopes = mem::replace(&mut self.a_scopes, scopes); + let result = self.relate(&kind, &b_kind); + self.a_scopes = old_a_scopes; + debug!("equate_var: complete, result = {:?}", result); + return result; + } + + // Not yet. Capture the value from the RHS and carry on. + self.canonical_var_values[var] = Some(ScopesAndKind { + scopes: self.b_scopes.clone(), + kind: b_kind, + }); + debug!( + "equate_var: capturing value {:?}", + self.canonical_var_values[var] + ); + + // FIXME -- technically, we should add some sort of + // assertion that this value can be named in the universe + // of the canonical variable. But in practice these + // canonical variables only arise presently in cases where + // they are in the root universe and the main typeck has + // ensured there are no universe errors. So we just kind + // of over look this right now. + Ok(b_kind) + } +} + +impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> + for TypeRelating<'cx, 'bccx, 'gcx, 'tcx> +{ + fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn tag(&self) -> &'static str { + "nll::subtype" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + a: &T, + b: &T, + ) -> RelateResult<'tcx, T> { + debug!( + "relate_with_variance(variance={:?}, a={:?}, b={:?})", + variance, a, b + ); + + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + debug!( + "relate_with_variance: ambient_variance = {:?}", + self.ambient_variance + ); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!("relate_with_variance: r={:?}", r); + + Ok(r) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + // Watch out for the case that we are matching a `?T` against the + // right-hand side. + if let ty::TyInfer(ty::CanonicalTy(var)) = a.sty { + self.equate_var(var, b.into())?; + Ok(a) + } else { + debug!( + "tys(a={:?}, b={:?}, variance={:?})", + a, b, self.ambient_variance + ); + + relate::super_relate_tys(self, a, b) + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + if let Some(&mut BorrowCheckContext { + universal_regions, .. + }) = self.borrowck_context + { + if let ty::ReCanonical(var) = a { + self.equate_var(*var, b.into())?; + return Ok(a); + } + + debug!( + "regions(a={:?}, b={:?}, variance={:?})", + a, b, self.ambient_variance + ); + + let v_a = self.replace_bound_region(universal_regions, a, &self.a_scopes); + let v_b = self.replace_bound_region(universal_regions, b, &self.b_scopes); + + debug!("regions: v_a = {:?}", v_a); + debug!("regions: v_b = {:?}", v_b); + + if self.ambient_covariance() { + // Covariance: a <= b. Hence, `b: a`. + self.push_outlives(v_b, v_a); + } + + if self.ambient_contravariance() { + // Contravariant: b <= a. Hence, `a: b`. + self.push_outlives(v_a, v_b); + } + } + + Ok(a) + } + + fn binders( + &mut self, + a: &ty::Binder, + b: &ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + where + T: Relate<'tcx>, + { + // We want that + // + // ``` + // for<'a> fn(&'a u32) -> &'a u32 <: + // fn(&'b u32) -> &'b u32 + // ``` + // + // but not + // + // ``` + // fn(&'a u32) -> &'a u32 <: + // for<'b> fn(&'b u32) -> &'b u32 + // ``` + // + // We therefore proceed as follows: + // + // - Instantiate binders on `b` universally, yielding a universe U1. + // - Instantiate binders on `a` existentially in U1. + + debug!( + "binders({:?}: {:?}, ambient_variance={:?})", + a, b, self.ambient_variance + ); + + if self.ambient_covariance() { + // Covariance, so we want `for<..> A <: for<..> B` -- + // therefore we compare any instantiation of A (i.e., A + // instantiated with existentials) against every + // instantiation of B (i.e., B instantiated with + // universals). + + let b_scope = self.create_scope(b, UniversallyQuantified(true)); + let a_scope = self.create_scope(a, UniversallyQuantified(false)); + + debug!("binders: a_scope = {:?} (existential)", a_scope); + debug!("binders: b_scope = {:?} (universal)", b_scope); + + self.b_scopes.push(b_scope); + self.a_scopes.push(a_scope); + + // FIXME -- to be fully correct, we would set the ambient + // variance to Covariant here. As is, we will sometimes + // propagate down an ambient variance of Equal -- this in + // turn causes us to report errors in some cases where + // types perhaps *ought* to be equal. See the + // `hr-fn-aau-eq-abu.rs` test for an example. Fixing this + // though is a bit nontrivial: in particular, it would + // require a more involved handling of canonical + // variables, since we would no longer be able to rely on + // having an `==` relationship for canonical variables. + + self.relate(a.skip_binder(), b.skip_binder())?; + + self.b_scopes.pop().unwrap(); + self.a_scopes.pop().unwrap(); + } + + if self.ambient_contravariance() { + // Contravariance, so we want `for<..> A :> for<..> B` + // -- therefore we compare every instantiation of A (i.e., + // A instantiated with universals) against any + // instantiation of B (i.e., B instantiated with + // existentials). Opposite of above. + + let a_scope = self.create_scope(a, UniversallyQuantified(true)); + let b_scope = self.create_scope(b, UniversallyQuantified(false)); + + debug!("binders: a_scope = {:?} (universal)", a_scope); + debug!("binders: b_scope = {:?} (existential)", b_scope); + + self.a_scopes.push(a_scope); + self.b_scopes.push(b_scope); + + self.relate(a.skip_binder(), b.skip_binder())?; + + self.b_scopes.pop().unwrap(); + self.a_scopes.pop().unwrap(); + } + + Ok(a.clone()) + } +} + +struct ScopeInstantiator<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> { + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + // The debruijn index of the scope we are instantiating. + target_index: ty::DebruijnIndex, + universally_quantified: UniversallyQuantified, + bound_region_scope: &'cx mut BoundRegionScope, +} + +impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> { + fn visit_binder>(&mut self, t: &ty::Binder) -> bool { + self.target_index.shift_in(1); + t.super_visit_with(self); + self.target_index.shift_out(1); + + false + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + let ScopeInstantiator { + infcx, + universally_quantified, + .. + } = *self; + + match r { + ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => { + self.bound_region_scope.map.entry(*br).or_insert_with(|| { + let origin = if universally_quantified.0 { + NLLRegionVariableOrigin::BoundRegion(infcx.create_subuniverse()) + } else { + NLLRegionVariableOrigin::Existential + }; + infcx.next_nll_region_var(origin).to_region_vid() + }); + } + + _ => {} + } + + false + } +} diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index f1591535fa1aa..5de316a664033 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -487,7 +487,7 @@ enum TestKind<'tcx> { // test the branches of enum Switch { adt_def: &'tcx ty::AdtDef, - variants: BitVector, + variants: BitVector, }, // test the branches of enum diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index e2b460f69fd74..3ff209c872fac 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -149,7 +149,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn add_variants_to_switch<'pat>(&mut self, test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, - variants: &mut BitVector) + variants: &mut BitVector) -> bool { let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index d58affbae75ed..382248c2d15dc 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -27,6 +27,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(core_intrinsics)] #![feature(decl_macro)] #![feature(fs_read_write)] +#![feature(in_band_lifetimes)] #![feature(macro_vis_matcher)] #![feature(exhaustive_patterns)] #![feature(range_contains)] @@ -36,6 +37,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(specialization)] #![feature(try_trait)] #![feature(unicode_internals)] +#![feature(step_trait)] #![recursion_limit="256"] diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 2f983e25acd74..5f05783b15cce 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -231,7 +231,7 @@ pub struct InliningMap<'tcx> { // Contains one bit per mono item in the `targets` field. That bit // is true if that mono item needs to be inlined into every CGU. - inlines: BitVector, + inlines: BitVector, } impl<'tcx> InliningMap<'tcx> { diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 97467e003850a..6a9258fe2c918 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -292,8 +292,10 @@ fn make_generator_state_argument_indirect<'a, 'tcx>( DerefArgVisitor.visit_mir(mir); } -fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>, - mir: &mut Mir<'tcx>) -> Local { +fn replace_result_variable<'tcx>( + ret_ty: Ty<'tcx>, + mir: &mut Mir<'tcx>, +) -> Local { let source_info = source_info(mir); let new_ret = LocalDecl { mutability: Mutability::Mut, @@ -306,7 +308,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>, }; let new_ret_local = Local::new(mir.local_decls.len()); mir.local_decls.push(new_ret); - mir.local_decls.swap(0, new_ret_local.index()); + mir.local_decls.swap(RETURN_PLACE, new_ret_local); RenameLocalVisitor { from: RETURN_PLACE, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 4e1129ea7e945..8a12a604ef202 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -116,7 +116,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'tcx>, local_qualif: IndexVec>, qualif: Qualif, - const_fn_arg_vars: BitVector, + const_fn_arg_vars: BitVector, temp_promotion_state: IndexVec, promotion_candidates: Vec } @@ -344,7 +344,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // Make sure there are no extra unassigned variables. self.qualif = Qualif::NOT_CONST; for index in mir.vars_iter() { - if !self.const_fn_arg_vars.contains(index.index()) { + if !self.const_fn_arg_vars.contains(index) { debug!("unassigned variable {:?}", index); self.assign(&Place::Local(index), Location { block: bb, @@ -1021,7 +1021,7 @@ This does not pose a problem by itself because they can't be accessed directly." // Check the allowed const fn argument forms. if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) { if self.mir.local_kind(index) == LocalKind::Var && - self.const_fn_arg_vars.insert(index.index()) && + self.const_fn_arg_vars.insert(index) && !self.tcx.sess.features_untracked().const_let { // Direct use of an argument is permitted. diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index 680b60b972841..a7ef93eaec6b9 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -11,7 +11,6 @@ use rustc::ty::TyCtxt; use rustc::mir::*; use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::Idx; use transform::{MirPass, MirSource}; use util::patch::MirPatch; @@ -42,9 +41,12 @@ impl MirPass for RemoveNoopLandingPads { } impl RemoveNoopLandingPads { - fn is_nop_landing_pad(&self, bb: BasicBlock, mir: &Mir, nop_landing_pads: &BitVector) - -> bool - { + fn is_nop_landing_pad( + &self, + bb: BasicBlock, + mir: &Mir, + nop_landing_pads: &BitVector, + ) -> bool { for stmt in &mir[bb].statements { match stmt.kind { StatementKind::ReadForMatch(_) | @@ -79,8 +81,8 @@ impl RemoveNoopLandingPads { TerminatorKind::SwitchInt { .. } | TerminatorKind::FalseEdges { .. } | TerminatorKind::FalseUnwind { .. } => { - terminator.successors().all(|succ| { - nop_landing_pads.contains(succ.index()) + terminator.successors().all(|&succ| { + nop_landing_pads.contains(succ) }) }, TerminatorKind::GeneratorDrop | @@ -117,7 +119,7 @@ impl RemoveNoopLandingPads { for bb in postorder { debug!(" processing {:?}", bb); for target in mir[bb].terminator_mut().successors_mut() { - if *target != resume_block && nop_landing_pads.contains(target.index()) { + if *target != resume_block && nop_landing_pads.contains(*target) { debug!(" folding noop jump to {:?} to resume block", target); *target = resume_block; jumps_folded += 1; @@ -138,7 +140,7 @@ impl RemoveNoopLandingPads { let is_nop_landing_pad = self.is_nop_landing_pad(bb, mir, &nop_landing_pads); if is_nop_landing_pad { - nop_landing_pads.insert(bb.index()); + nop_landing_pads.insert(bb); } debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 7e23a5cb1a895..6b8d5a1489388 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -288,15 +288,15 @@ impl MirPass for SimplifyLocals { let mut marker = DeclMarker { locals: BitVector::new(mir.local_decls.len()) }; marker.visit_mir(mir); // Return pointer and arguments are always live - marker.locals.insert(RETURN_PLACE.index()); + marker.locals.insert(RETURN_PLACE); for arg in mir.args_iter() { - marker.locals.insert(arg.index()); + marker.locals.insert(arg); } // We may need to keep dead user variables live for debuginfo. if tcx.sess.opts.debuginfo == FullDebugInfo { for local in mir.vars_iter() { - marker.locals.insert(local.index()); + marker.locals.insert(local); } } @@ -308,35 +308,38 @@ impl MirPass for SimplifyLocals { } /// Construct the mapping while swapping out unused stuff out from the `vec`. -fn make_local_map<'tcx, I: Idx, V>(vec: &mut IndexVec, mask: BitVector) -> Vec { - let mut map: Vec = ::std::iter::repeat(!0).take(vec.len()).collect(); - let mut used = 0; +fn make_local_map<'tcx, V>( + vec: &mut IndexVec, + mask: BitVector, +) -> IndexVec> { + let mut map: IndexVec> = IndexVec::from_elem(None, &*vec); + let mut used = Local::new(0); for alive_index in mask.iter() { - map[alive_index] = used; + map[alive_index] = Some(used); if alive_index != used { vec.swap(alive_index, used); } - used += 1; + used.increment_by(1); } - vec.truncate(used); + vec.truncate(used.index()); map } struct DeclMarker { - pub locals: BitVector, + pub locals: BitVector, } impl<'tcx> Visitor<'tcx> for DeclMarker { fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) { // ignore these altogether, they get removed along with their otherwise unused decls. if ctx != PlaceContext::StorageLive && ctx != PlaceContext::StorageDead { - self.locals.insert(local.index()); + self.locals.insert(*local); } } } struct LocalUpdater { - map: Vec, + map: IndexVec>, } impl<'tcx> MutVisitor<'tcx> for LocalUpdater { @@ -345,7 +348,7 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater { data.statements.retain(|stmt| { match stmt.kind { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - self.map[l.index()] != !0 + self.map[l].is_some() } _ => true } @@ -353,6 +356,6 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater { self.super_basic_block_data(block, data); } fn visit_local(&mut self, l: &mut Local, _: PlaceContext<'tcx>, _: Location) { - *l = Local::new(self.map[l.index()]); + *l = self.map[*l].unwrap(); } } diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 5cca3e55259d0..adc0249a40cfc 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..=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]} +// | '_#0r | U0 | {bb0[0..=127], '_#0r} +// | '_#1r | U0 | {bb0[0..=127], '_#1r} +// | '_#2r | U0 | {bb0[0..=127], '_#2r} +// | '_#3r | U0 | {bb0[0..=127], '_#3r} +// | '_#4r | U0 | {bb0[0..=127], '_#4r} +// | '_#5r | U0 | {bb0[0..=127], '_#1r} +// | '_#6r | U0 | {bb0[0..=127], '_#2r} +// | '_#7r | U0 | {bb0[0..=127], '_#1r} +// | '_#8r | U0 | {bb0[0..=127], '_#3r} // | // ... // 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 94a6a9799cf9d..8a7ea8962fc5d 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[4], bb0[8..=17]} +// | '_#7r | U0 | {bb0[4], bb0[8..=17]} // ... -// | '_#9r | {bb0[10], bb0[14..=17]} +// | '_#9r | U0 | {bb0[10], bb0[14..=17]} // ... // let _4: &'_#9r mut i32; // ... diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index a8dd6c73ef017..e554024efef43 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..=3], bb3[0..=1]} -// | '_#3r | {bb2[1..=3], bb3[0..=1]} -// | '_#4r | {bb2[3], bb3[0..=1]} +// | '_#2r | U0 | {bb2[0..=3], bb3[0..=1]} +// | '_#3r | U0 | {bb2[1..=3], bb3[0..=1]} +// | '_#4r | U0 | {bb2[3], bb3[0..=1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // let _6: &'_#4r usize; diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr b/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr index 2284f0784c545..92225369c9271 100644 --- a/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr +++ b/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr @@ -2,28 +2,37 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:20:25 | LL | (self.func)(arg) //~ ERROR cannot borrow - | ------------^^^- - | | | - | | mutable borrow starts here in previous iteration of loop - | borrow later used here + | ^^^ mutable borrow starts here in previous iteration of loop + | +note: borrowed value must be valid for the lifetime 'a as defined on the impl at 17:6... + --> $DIR/mut-borrow-in-loop.rs:17:6 + | +LL | impl<'a, T : 'a> FuncWrapper<'a, T> { + | ^^ error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:26:25 | LL | (self.func)(arg) //~ ERROR cannot borrow - | ------------^^^- - | | | - | | mutable borrow starts here in previous iteration of loop - | borrow later used here + | ^^^ mutable borrow starts here in previous iteration of loop + | +note: borrowed value must be valid for the lifetime 'a as defined on the impl at 17:6... + --> $DIR/mut-borrow-in-loop.rs:17:6 + | +LL | impl<'a, T : 'a> FuncWrapper<'a, T> { + | ^^ error[E0499]: cannot borrow `*arg` as mutable more than once at a time --> $DIR/mut-borrow-in-loop.rs:33:25 | LL | (self.func)(arg) //~ ERROR cannot borrow - | ------------^^^- - | | | - | | mutable borrow starts here in previous iteration of loop - | borrow later used here + | ^^^ mutable borrow starts here in previous iteration of loop + | +note: borrowed value must be valid for the lifetime 'a as defined on the impl at 17:6... + --> $DIR/mut-borrow-in-loop.rs:17:6 + | +LL | impl<'a, T : 'a> FuncWrapper<'a, T> { + | ^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr index 4c0b3a5d93120..c6f8d2e519ca1 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr @@ -10,19 +10,21 @@ warning: not reporting region error due to nll LL | self.x.iter().map(|a| a.0) | ^^^^ -error: unsatisfied lifetime constraints +error: borrowed data escapes outside of closure --> $DIR/static-return-lifetime-infered.rs:17:9 | LL | fn iter_values_anon(&self) -> impl Iterator { - | - let's call the lifetime of this reference `'1` + | ----- `self` is a reference that is only valid in the closure body LL | self.x.iter().map(|a| a.0) - | ^^^^^^ cast requires that `'1` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` escapes the closure body here -error: unsatisfied lifetime constraints +error: borrowed data escapes outside of closure --> $DIR/static-return-lifetime-infered.rs:21:9 | +LL | fn iter_values<'a>(&'a self) -> impl Iterator { + | -------- `self` is a reference that is only valid in the closure body LL | self.x.iter().map(|a| a.0) - | ^^^^^^ cast requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` escapes the closure body here error: aborting due to 2 previous errors diff --git a/src/test/ui/issue-40288-2.nll.stderr b/src/test/ui/issue-40288-2.nll.stderr index fcff5c6a8edad..1d2b26603426f 100644 --- a/src/test/ui/issue-40288-2.nll.stderr +++ b/src/test/ui/issue-40288-2.nll.stderr @@ -35,20 +35,22 @@ LL | let mut out = Struct { head: x, _tail: [()] }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0621]: explicit lifetime required in the type of `y` - --> $DIR/issue-40288-2.rs:14:9 + --> $DIR/issue-40288-2.rs:17:9 | LL | fn lifetime_transmute_slice<'a, T: ?Sized>(x: &'a T, y: &T) -> &'a T { | - consider changing the type of `y` to `&'a T` -LL | let mut out = [x]; - | ^^^^^^^ lifetime `'a` required +... +LL | slice[0] = y; + | ^^^^^^^^^^^^ lifetime `'a` required error[E0621]: explicit lifetime required in the type of `y` - --> $DIR/issue-40288-2.rs:29:9 + --> $DIR/issue-40288-2.rs:32:9 | LL | fn lifetime_transmute_struct<'a, T: ?Sized>(x: &'a T, y: &T) -> &'a T { | - consider changing the type of `y` to `&'a T` -LL | let mut out = Struct { head: x, _tail: [()] }; - | ^^^^^^^ lifetime `'a` required +... +LL | dst.head = y; + | ^^^^^^^^^^^^ lifetime `'a` required error: aborting due to 2 previous errors diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr index 1e45914138de5..4d9517eca6032 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr @@ -5,12 +5,12 @@ LL | if x > y { x } else { y } //~ ERROR explicit lifetime | ^ error[E0621]: explicit lifetime required in parameter type - --> $DIR/ex1-return-one-existing-name-if-else-3.rs:11:13 + --> $DIR/ex1-return-one-existing-name-if-else-3.rs:11:16 | LL | fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 { - | -^---- - | || - | |lifetime `'a` required + | ----^- + | | | + | | lifetime `'a` required | consider changing type to `(&'a i32, &'a i32)` error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr index a51d9307d074d..087c9eb389b55 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr @@ -10,7 +10,7 @@ error[E0621]: explicit lifetime required in the type of `x` LL | fn foo<'a>(x: Ref, y: &mut Vec>) { | - consider changing the type of `x` to `Ref<'a, i32>` LL | y.push(x); //~ ERROR explicit lifetime - | ^ lifetime `'a` required + | ^^^^^^^^^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr index e50fd74faf4ba..80192af221755 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr @@ -10,7 +10,7 @@ error[E0621]: explicit lifetime required in the type of `y` LL | fn foo<'a>(x: &mut Vec>, y: Ref) { | - consider changing the type of `y` to `Ref<'a, i32>` LL | x.push(y); //~ ERROR explicit lifetime - | ^ lifetime `'a` required + | ^^^^^^^^^ lifetime `'a` required error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr index 283192c684392..4b4fdde940f7e 100644 --- a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr +++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo(x: &mut Vec>, y: Ref) { | -------- -------- these two types are declared with different lifetimes... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr index 2ca202b402cee..f55fd291249c6 100644 --- a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr +++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr @@ -5,12 +5,13 @@ LL | let z = Ref { data: y.data }; | ^^^ error[E0623]: lifetime mismatch - --> $DIR/ex2c-push-inference-variable.rs:16:9 + --> $DIR/ex2c-push-inference-variable.rs:17:5 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | ------------ ------------ these two types are declared with different lifetimes... LL | let z = Ref { data: y.data }; - | ^ ...but data from `y` flows into `x` here +LL | x.push(z); //~ ERROR lifetime mismatch + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr index 712c25f8929d4..85b5f3e890008 100644 --- a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr +++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr @@ -5,12 +5,13 @@ LL | let b = Ref { data: y.data }; | ^^^ error[E0623]: lifetime mismatch - --> $DIR/ex2d-push-inference-variable-2.rs:16:9 + --> $DIR/ex2d-push-inference-variable-2.rs:18:5 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | ------------ ------------ these two types are declared with different lifetimes... -LL | let a: &mut Vec> = x; //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here +... +LL | a.push(b); + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr index 351966902a4fb..7e5182a5d30c9 100644 --- a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr +++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr @@ -5,12 +5,13 @@ LL | let b = Ref { data: y.data }; | ^^^ error[E0623]: lifetime mismatch - --> $DIR/ex2e-push-inference-variable-3.rs:16:9 + --> $DIR/ex2e-push-inference-variable-3.rs:18:5 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | ------------ ------------ these two types are declared with different lifetimes... -LL | let a: &mut Vec> = x; //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here +... +LL | Vec::push(a, b); + | ^^^^^^^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr index da171577e2d8b..36317c4570b98 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr @@ -5,12 +5,12 @@ LL | *v = x; //~ ERROR lifetime mismatch | ^ error[E0623]: lifetime mismatch - --> $DIR/ex3-both-anon-regions-2.rs:11:14 + --> $DIR/ex3-both-anon-regions-2.rs:12:5 | LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) { - | ^^^^^^^^^ --- --- these two types are declared with different lifetimes... - | | - | ...but data from `x` flows here + | --- --- these two types are declared with different lifetimes... +LL | *v = x; //~ ERROR lifetime mismatch + | ^^^^^^ ...but data from `x` flows here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr index 102981977e557..c43c4ce3a0c21 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr @@ -11,20 +11,20 @@ LL | z.push((x,y)); //~ ERROR lifetime mismatch | ^ error[E0623]: lifetime mismatch - --> $DIR/ex3-both-anon-regions-3.rs:11:33 + --> $DIR/ex3-both-anon-regions-3.rs:12:5 | LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { - | --- ^ --- these two types are declared with different lifetimes... - | | - | ...but data flows into `z` here + | --- --- these two types are declared with different lifetimes... +LL | z.push((x,y)); //~ ERROR lifetime mismatch + | ^^^^^^^^^^^^^ ...but data flows into `z` here error[E0623]: lifetime mismatch - --> $DIR/ex3-both-anon-regions-3.rs:11:33 + --> $DIR/ex3-both-anon-regions-3.rs:12:5 | LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { - | --- ^ --- these two types are declared with different lifetimes... - | | - | ...but data flows into `z` here + | --- --- these two types are declared with different lifetimes... +LL | z.push((x,y)); //~ ERROR lifetime mismatch + | ^^^^^^^^^^^^^ ...but data flows into `z` here error: aborting due to 2 previous errors diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr index 9d1f6a3e36f1b..2a5729952e33d 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr @@ -11,7 +11,7 @@ LL | fn foo<'a, 'b>(mut x: Vec>, y: Ref<'b>) | ------- ------- these two types are declared with different lifetimes... ... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr index 5df93fd5547c7..6efc8d3da0677 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo<'a, 'b>(mut x: Vec>, y: Ref<'b>) { | ------- ------- these two types are declared with different lifetimes... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr index cd602cf950b18..0f555020822cb 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo(mut x: Vec, y: Ref) { | --- --- these two types are declared with different lifetimes... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr index 52c90839c32af..4400644e7fb65 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo<'a,'b>(x: &mut Vec<&'a u8>, y: &'b u8) { | ------ ------ these two types are declared with different lifetimes... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr index d5bba6649a2c7..a0aa1e28d9bc0 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) { | --- --- these two types are declared with different lifetimes... LL | y.push(z); //~ ERROR lifetime mismatch - | ^ ...but data from `z` flows into `y` here + | ^^^^^^^^^ ...but data from `z` flows into `y` here error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable --> $DIR/ex3-both-anon-regions-using-fn-items.rs:11:3 diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr index 4d54f6fe0375c..5d4492701beb3 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo(x: &mut Vec<&u8>, y: &u8) { | --- --- these two types are declared with different lifetimes... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr index 0608b3be8b33a..37b79cee72f75 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo(x:Box , y: Vec<&u8>, z: &u8) { | --- --- these two types are declared with different lifetimes... LL | y.push(z); //~ ERROR lifetime mismatch - | ^ ...but data from `z` flows into `y` here + | ^^^^^^^^^ ...but data from `z` flows into `y` here error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:11:3 diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr index c25eedc770d48..c11d81a4c13da 100644 --- a/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr +++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr @@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch LL | fn foo(x: &mut Vec<&u8>, y: &u8) { | --- --- these two types are declared with different lifetimes... LL | x.push(y); //~ ERROR lifetime mismatch - | ^ ...but data from `y` flows into `x` here + | ^^^^^^^^^ ...but data from `y` flows into `x` here error: aborting due to previous error 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 39050864768ae..b879f9a33986d 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 @@ -52,9 +52,9 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell cell_c, |_outlives1, _outlives2, _outlives3, x, y| { // Only works if 'x: 'y: - let p = x.get(); //~ ERROR + let p = x.get(); //~^ WARN not reporting region error due to nll - demand_y(x, y, p) + demand_y(x, y, p) //~ ERROR }, ); } 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 6588cbe8bdf26..a7a50a3a02981 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 @@ -1,28 +1,28 @@ warning: not reporting region error due to nll --> $DIR/propagate-approximated-fail-no-postdom.rs:55:21 | -LL | let p = x.get(); //~ ERROR +LL | let p = x.get(); | ^^^^^^^ error: unsatisfied lifetime constraints - --> $DIR/propagate-approximated-fail-no-postdom.rs:55:21 + --> $DIR/propagate-approximated-fail-no-postdom.rs:57:13 | LL | |_outlives1, _outlives2, _outlives3, x, y| { | ---------- ---------- lifetime `'2` appears in this argument | | | lifetime `'1` appears in this argument -LL | // Only works if 'x: 'y: -LL | let p = x.get(); //~ ERROR - | ^^^^^^^ argument requires that `'1` must outlive `'2` +... +LL | demand_y(x, y, p) //~ ERROR + | ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9 | LL | / |_outlives1, _outlives2, _outlives3, x, y| { LL | | // Only works if 'x: 'y: -LL | | let p = x.get(); //~ ERROR +LL | | let p = x.get(); LL | | //~^ WARN not reporting region error due to nll -LL | | demand_y(x, y, p) +LL | | demand_y(x, y, p) //~ ERROR LL | | }, | |_________^ | 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 840b4071a8776..d713a37fa9f41 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -24,14 +24,19 @@ LL | | }); = note: where '_#1r: '_#2r error[E0623]: lifetime mismatch - --> $DIR/propagate-approximated-ref.rs:53:29 + --> $DIR/propagate-approximated-ref.rs:53:5 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | ------- ------- - | | - | these two types are declared with different lifetimes... -LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ^^^^^^^ ...but data from `cell_a` flows into `cell_b` here +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | ------- ------- + | | + | these two types are declared with different lifetimes... +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | //~^ ERROR lifetime mismatch +LL | | +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) //~ WARNING not reporting region error due to nll +LL | | }); + | |______^ ...but data from `cell_a` flows into `cell_b` here note: No external requirements --> $DIR/propagate-approximated-ref.rs:52: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 d51ba8201aaa6..b8e8fae14b005 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 @@ -5,7 +5,7 @@ LL | foo(cell, |cell_a, cell_x| { | ^^^ error: borrowed data escapes outside of closure - --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:20 + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:9 | LL | foo(cell, |cell_a, cell_x| { | ------ ------ `cell_x` is a reference that is only valid in the closure body @@ -13,7 +13,7 @@ LL | foo(cell, |cell_a, cell_x| { | `cell_a` is declared here, outside of the closure body LL | //~^ WARNING not reporting region error due to nll LL | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure - | ^^^^^^^^^^^^ `cell_x` escapes the closure body here + | ^^^^^^^^^^^^^^^^^^^^^^^^ `cell_x` escapes the closure body here note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15 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 cf5f4d415b669..f6ad6e46c6299 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -24,14 +24,19 @@ LL | | }); = note: where '_#1r: '_#2r error[E0623]: lifetime mismatch - --> $DIR/propagate-approximated-val.rs:46:29 + --> $DIR/propagate-approximated-val.rs:46:5 | -LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | ------- ------- - | | - | these two types are declared with different lifetimes... -LL | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { - | ^^^^^^ ...but data from `cell_a` flows into `cell_b` here +LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | ------- ------- + | | + | these two types are declared with different lifetimes... +LL | / establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { +LL | | //~^ ERROR lifetime mismatch +LL | | +LL | | // Only works if 'x: 'y: +LL | | demand_y(outlives1, outlives2, x.get()) //~ WARNING not reporting region error due to nll +LL | | }); + | |______^ ...but data from `cell_a` flows into `cell_b` here note: No external requirements --> $DIR/propagate-approximated-val.rs:45:1 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 c75b3e6670cdc..fb98c506c7d28 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,7 +5,7 @@ LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ error: unsatisfied lifetime constraints - --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:24 + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | --------- - lifetime `'1` appears in this argument @@ -13,7 +13,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | lifetime `'2` appears in this argument LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) - | ^^^^^^^ argument requires that `'1` must outlive `'2` + | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47 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 2465219ee552a..73d39a8502b64 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,7 +5,7 @@ LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ error: unsatisfied lifetime constraints - --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:24 + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { | ---------- ---------- lifetime `'2` appears in this argument @@ -13,7 +13,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y | lifetime `'1` appears in this argument LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) - | ^^^^^^^ argument requires that `'1` must outlive `'2` + | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47 diff --git a/src/test/compile-fail/mir_check_cast_closure.rs b/src/test/ui/nll/mir_check_cast_closure.rs similarity index 100% rename from src/test/compile-fail/mir_check_cast_closure.rs rename to src/test/ui/nll/mir_check_cast_closure.rs diff --git a/src/test/ui/nll/mir_check_cast_closure.stderr b/src/test/ui/nll/mir_check_cast_closure.stderr new file mode 100644 index 0000000000000..fc2a3c43f7589 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_closure.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to nll + --> $DIR/mir_check_cast_closure.rs:18:5 + | +LL | g + | ^ + +error: unsatisfied lifetime constraints + --> $DIR/mir_check_cast_closure.rs:16:28 + | +LL | let g: fn(_, _) -> _ = |_x, y| y; + | ^^^^^^^^^ cast requires that `'b` must outlive `'a` + +error: aborting due to previous error + diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/ui/nll/mir_check_cast_reify.rs similarity index 97% rename from src/test/compile-fail/mir_check_cast_reify.rs rename to src/test/ui/nll/mir_check_cast_reify.rs index f85104dff867f..3a530c1e7473d 100644 --- a/src/test/compile-fail/mir_check_cast_reify.rs +++ b/src/test/ui/nll/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 nll - //~| ERROR unsatisfied lifetime constraints f(x) + //~^ ERROR } fn main() {} diff --git a/src/test/ui/nll/mir_check_cast_reify.stderr b/src/test/ui/nll/mir_check_cast_reify.stderr new file mode 100644 index 0000000000000..13f90e1f159d2 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_reify.stderr @@ -0,0 +1,17 @@ +warning: not reporting region error due to nll + --> $DIR/mir_check_cast_reify.rs:46:25 + | +LL | let f: fn(_) -> _ = foo; + | ^^^ + +error: borrowed data escapes outside of closure + --> $DIR/mir_check_cast_reify.rs:48:5 + | +LL | fn bar<'a>(x: &'a u32) -> &'static u32 { + | - `x` is a reference that is only valid in the closure body +... +LL | f(x) + | ^^^^ `x` escapes the closure body here + +error: aborting due to previous error + diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/ui/nll/mir_check_cast_unsafe_fn.rs similarity index 94% rename from src/test/compile-fail/mir_check_cast_unsafe_fn.rs rename to src/test/ui/nll/mir_check_cast_unsafe_fn.rs index e90242f3f87da..4a840da028d81 100644 --- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs +++ b/src/test/ui/nll/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 nll - //~| ERROR unsatisfied lifetime constraints unsafe { g(input) } + //~^ ERROR } fn main() {} diff --git a/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr b/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr new file mode 100644 index 0000000000000..b08c6f32e6b43 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr @@ -0,0 +1,17 @@ +warning: not reporting region error due to nll + --> $DIR/mir_check_cast_unsafe_fn.rs:18:32 + | +LL | let g: unsafe fn(_) -> _ = f; + | ^ + +error: borrowed data escapes outside of closure + --> $DIR/mir_check_cast_unsafe_fn.rs:20:14 + | +LL | fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { + | ----- `input` is a reference that is only valid in the closure body +... +LL | unsafe { g(input) } + | ^^^^^^^^ `input` escapes the closure body here + +error: aborting due to previous error + diff --git a/src/test/compile-fail/mir_check_cast_unsize.rs b/src/test/ui/nll/mir_check_cast_unsize.rs similarity index 92% rename from src/test/compile-fail/mir_check_cast_unsize.rs rename to src/test/ui/nll/mir_check_cast_unsize.rs index d242186a6f7ea..695dddbf7e9d5 100644 --- a/src/test/compile-fail/mir_check_cast_unsize.rs +++ b/src/test/ui/nll/mir_check_cast_unsize.rs @@ -15,7 +15,8 @@ use std::fmt::Debug; fn bar<'a>(x: &'a u32) -> &'static dyn Debug { - x //~ ERROR unsatisfied lifetime constraints + //~^ ERROR unsatisfied lifetime constraints + x //~^ WARNING not reporting region error due to nll } diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr new file mode 100644 index 0000000000000..7bd0595f3b5cc --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_unsize.stderr @@ -0,0 +1,19 @@ +warning: not reporting region error due to nll + --> $DIR/mir_check_cast_unsize.rs:19:5 + | +LL | x + | ^ + +error: unsatisfied lifetime constraints + --> $DIR/mir_check_cast_unsize.rs:17:46 + | +LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug { + | ______________________________________________^ +LL | | //~^ ERROR unsatisfied lifetime constraints +LL | | x +LL | | //~^ WARNING not reporting region error due to nll +LL | | } + | |_^ return requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs new file mode 100644 index 0000000000000..84c305f5907d1 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning either argument CANNOT be upcast to one +// that returns always its first argument. +// +// compile-flags:-Zno-leak-check + +#![feature(nll)] + +fn make_it() -> for<'a> fn(&'a u32, &'a u32) -> &'a u32 { + panic!() +} + +fn main() { + let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + //~^ ERROR higher-ranked subtype error + drop(a); +} diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr new file mode 100644 index 0000000000000..e08d848b47140 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/hr-fn-aaa-as-aba.rs:24:58 + | +LL | let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs new file mode 100644 index 0000000000000..9b8268d9736aa --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs @@ -0,0 +1,38 @@ +// 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. + +// Test an interesting corner case that ought to be legal (though the +// current code actually gets it wrong, see below): a fn that takes +// two arguments that are references with the same lifetime is in fact +// equivalent to a fn that takes two references with distinct +// lifetimes. This is true because the two functions can call one +// another -- effectively, the single lifetime `'a` is just inferred +// to be the intersection of the two distinct lifetimes. +// +// FIXME: However, we currently reject this example with an error, +// because of how we handle binders and equality in `relate_tys`. +// +// compile-flags:-Zno-leak-check + +#![feature(nll)] + +use std::cell::Cell; + +fn make_cell_aa() -> Cell fn(&'a u32, &'a u32)> { + panic!() +} + +fn aa_eq_ab() { + let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); + //~^ ERROR higher-ranked subtype error + drop(a); +} + +fn main() { } diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr new file mode 100644 index 0000000000000..17e8a32cb2ad9 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/hr-fn-aau-eq-abu.rs:33:53 + | +LL | let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs new file mode 100644 index 0000000000000..4f73ca3a53921 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning always its first argument can be upcast to one +// that returns either first or second argument. +// +// compile-pass +// compile-flags:-Zno-leak-check + +#![feature(nll)] + +fn make_it() -> for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 { + panic!() +} + +fn main() { + let a: for<'a> fn(&'a u32, &'a u32) -> &'a u32 = make_it(); + drop(a); +} diff --git a/src/test/ui/nll/relate_tys/issue-48071.rs b/src/test/ui/nll/relate_tys/issue-48071.rs new file mode 100644 index 0000000000000..c2498cbe50f7e --- /dev/null +++ b/src/test/ui/nll/relate_tys/issue-48071.rs @@ -0,0 +1,38 @@ +// Copyright 2018 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. + +// Regression test for #48071. This test used to ICE because -- in +// the leak-check -- it would pass since we knew that the return type +// was `'static`, and hence `'static: 'a` was legal even for a +// placeholder region, but in NLL land it would fail because we had +// rewritten `'static` to a region variable. +// +// compile-pass + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(nll)] + +trait Foo { + fn foo(&self) { } +} + +impl Foo for () { +} + +type MakeFooFn = for<'a> fn(&'a u8) -> Box; + +fn make_foo(x: &u8) -> Box { + Box::new(()) +} + +fn main() { + let x: MakeFooFn = make_foo as MakeFooFn; +} diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr index 6385578698cf7..5b4c669c66ecd 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr @@ -23,13 +23,16 @@ LL | Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime | ^^^^^^^^^^^^^^^^^^^^^^ error: unsatisfied lifetime constraints - --> $DIR/dyn-trait-underscore.rs:18:5 + --> $DIR/dyn-trait-underscore.rs:16:52 | -LL | fn a(items: &[T]) -> Box> { - | - let's call the lifetime of this reference `'1` -LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` -LL | Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime - | ^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` +LL | fn a(items: &[T]) -> Box> { + | ________________-___________________________________^ + | | | + | | let's call the lifetime of this reference `'1` +LL | | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` +LL | | Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime +LL | | } + | |_^ return requires that `'1` must outlive `'static` error: aborting due to previous error