From 86334c711648cc17bba9955f955a6710159bd6f7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 05:29:37 -0500 Subject: remove unnecessary intermediate vector from `copy` --- .../borrow_check/nll/region_infer/mod.rs | 32 ++++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) (limited to 'src') 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 c926c7432bb..dfa7ce86875 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -471,35 +471,31 @@ impl<'tcx> RegionInferenceContext<'tcx> { changed |= new; let block_data = &mir[p.block]; - let successor_points = if p.statement_index < block_data.statements.len() { - vec![ - Location { - statement_index: p.statement_index + 1, - ..p - }, - ] + + let start_stack_len = stack.len(); + + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); } else { - block_data - .terminator() - .successors() - .iter() - .map(|&basic_block| { + stack.extend(block_data.terminator().successors().iter().map( + |&basic_block| { Location { statement_index: 0, block: basic_block, } - }) - .collect::>() - }; + }, + )); + } - if successor_points.is_empty() { + if stack.len() == start_stack_len { // If we reach the END point in the graph, then copy // over any skolemized end points in the `from_region` // and make sure they are included in the `to_region`. changed |= inferred_values.add_universal_regions_outlived_by(from_region, to_region); - } else { - stack.extend(successor_points); } } -- cgit 1.4.1-3-g733a5 From a0f0392a6d20dda6fcf7169a81688a0f8e1b51da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 05:49:27 -0500 Subject: rename `copy` to `dfs` and make it customizable --- .../borrow_check/nll/region_infer/dfs.rs | 217 +++++++++++++++++++++ .../borrow_check/nll/region_infer/mod.rs | 83 ++------ 2 files changed, 230 insertions(+), 70 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/region_infer/dfs.rs (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs new file mode 100644 index 00000000000..c8ac0fa233f --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -0,0 +1,217 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Module defining the `dfs` method on `RegionInferenceContext`, along with +//! its associated helper traits. + +use rustc::mir::{Location, Mir}; +use rustc::ty::RegionVid; +use rustc_data_structures::fx::FxHashSet; +use super::RegionInferenceContext; +use super::values::{RegionElementIndex, RegionValues, RegionValueElements}; + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Function used to satisfy or test a `R1: R2 @ P` + /// constraint. The core idea is that it performs a DFS starting + /// from `P`. The precise actions *during* that DFS depend on the + /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more + /// details. + /// + /// Returns: + /// + /// - `Ok(true)` if the walk was completed and something changed + /// along the way; + /// - `Ok(false)` if the walk was completed with no changes; + /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the + /// value that `op` returned. + pub(super) fn dfs(&self, mir: &Mir<'tcx>, mut op: C) -> Result + where + C: DfsOp, + { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(op.start_point()); + while let Some(p) = stack.pop() { + let point_index = self.elements.index(p); + + if !op.source_region_contains(point_index) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + debug!(" already visited"); + continue; + } + + let new = op.add_to_target_region(point_index)?; + changed |= new; + + let block_data = &mir[p.block]; + + let start_stack_len = stack.len(); + + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); + } else { + stack.extend(block_data.terminator().successors().iter().map( + |&basic_block| { + Location { + statement_index: 0, + block: basic_block, + } + }, + )); + } + + if stack.len() == start_stack_len { + // If we reach the END point in the graph, then copy + // over any skolemized end points in the `from_region` + // and make sure they are included in the `to_region`. + changed |= op.add_universal_regions_outlived_by_source_to_target()?; + } + } + + Ok(changed) + } +} + +/// Customizes the operation of the `dfs` function. This function is +/// used during inference to satisfy a `R1: R2 @ P` constraint. +pub(super) trait DfsOp { + /// If this op stops the walk early, what type does it propagate? + type Early; + + /// Returns the point from which to start the DFS. + fn start_point(&self) -> Location; + + /// Returns true if the source region contains the given point. + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool; + + /// Adds the given point to the target region, returning true if + /// something has changed. Returns `Err` if we should abort the + /// walk early. + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result; + + /// Adds all universal regions in the source region to the target region, returning + /// true if something has changed. + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result; +} + +/// Used during inference to enforce a `R1: R2 @ P` constraint. For +/// each point Q we reach along the DFS, we check if Q is in R2 (the +/// "source region"). If not, we stop the walk. Otherwise, we add Q to +/// R1 (the "target region") and continue to Q's successors. If we +/// reach the end of the graph, then we add any universal regions from +/// R2 into R1. +pub(super) struct CopyFromSourceToTarget<'v> { + pub source_region: RegionVid, + pub target_region: RegionVid, + pub inferred_values: &'v mut RegionValues, + pub constraint_point: Location, +} + +impl<'v> DfsOp for CopyFromSourceToTarget<'v> { + /// We never stop the walk early. + type Early = !; + + fn start_point(&self) -> Location { + self.constraint_point + } + + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { + self.inferred_values + .contains(self.source_region, point_index) + } + + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result { + Ok(self.inferred_values.add(self.target_region, point_index)) + } + + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result { + Ok( + self.inferred_values + .add_universal_regions_outlived_by(self.source_region, self.target_region), + ) + } +} + +/// Used after inference to *test* a `R1: R2 @ P` constraint. For +/// each point Q we reach along the DFS, we check if Q in R2 is also +/// contained in R1. If not, we abort the walk early with an `Err` +/// condition. Similarly, if we reach the end of the graph and find +/// that R1 contains some universal region that R2 does not contain, +/// we abort the walk early. +#[allow(dead_code)] // TODO +pub(super) struct TestTarget<'v> { + source_region: RegionVid, + target_region: RegionVid, + elements: &'v RegionValueElements, + inferred_values: &'v RegionValues, + constraint_point: Location, +} + +#[allow(dead_code)] // TODO +impl<'v> DfsOp for TestTarget<'v> { + /// The element that was not found within R2. + type Early = RegionElementIndex; + + fn start_point(&self) -> Location { + self.constraint_point + } + + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { + self.inferred_values + .contains(self.source_region, point_index) + } + + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result { + if !self.inferred_values + .contains(self.target_region, point_index) + { + return Err(point_index); + } + + Ok(false) + } + + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result { + for ur in self.inferred_values + .universal_regions_outlived_by(self.source_region) + { + if !self.inferred_values.contains(self.target_region, ur) { + return Err(self.elements.index(ur)); + } + } + + Ok(false) + } +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index dfa7ce86875..6d76398b941 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -18,12 +18,13 @@ use rustc::infer::region_constraints::VarOrigins; use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::fx::FxHashSet; use std::fmt; use std::rc::Rc; use syntax_pos::Span; mod annotation; +mod dfs; +use self::dfs::CopyFromSourceToTarget; mod dump_mir; mod graphviz; mod values; @@ -421,14 +422,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Grow the value as needed to accommodate the // outlives constraint. - - if self.copy( - &mut inferred_values, + let Ok(made_changes) = self.dfs( mir, - constraint.sub, - constraint.sup, - constraint.point, - ) { + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + }, + ); + + if made_changes { debug!("propagate_constraints: sub={:?}", constraint.sub); debug!("propagate_constraints: sup={:?}", constraint.sup); changed = true; @@ -440,68 +444,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } - fn copy( - &self, - inferred_values: &mut RegionValues, - mir: &Mir<'tcx>, - from_region: RegionVid, - to_region: RegionVid, - constraint_point: Location, - ) -> bool { - let mut changed = false; - - let mut stack = vec![]; - let mut visited = FxHashSet(); - - stack.push(constraint_point); - while let Some(p) = stack.pop() { - let point_index = self.elements.index(p); - - if !inferred_values.contains(from_region, point_index) { - debug!(" not in from-region"); - continue; - } - - if !visited.insert(p) { - debug!(" already visited"); - continue; - } - - let new = inferred_values.add(to_region, point_index); - changed |= new; - - let block_data = &mir[p.block]; - - let start_stack_len = stack.len(); - - if p.statement_index < block_data.statements.len() { - stack.push(Location { - statement_index: p.statement_index + 1, - ..p - }); - } else { - stack.extend(block_data.terminator().successors().iter().map( - |&basic_block| { - Location { - statement_index: 0, - block: basic_block, - } - }, - )); - } - - if stack.len() == start_stack_len { - // If we reach the END point in the graph, then copy - // over any skolemized end points in the `from_region` - // and make sure they are included in the `to_region`. - changed |= - inferred_values.add_universal_regions_outlived_by(from_region, to_region); - } - } - - changed - } - /// Tries to finds a good span to blame for the fact that `fr1` /// contains `fr2`. fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { @@ -647,3 +589,4 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { } } } + -- cgit 1.4.1-3-g733a5 From cd564d20ff521d56ad3f01fe0e0588d9487c0c13 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 06:16:52 -0500 Subject: only propagate ClosureRegionRequirements if non-trivial Before, we would always have a `Some` ClosureRegionRequirements if we were inferring values for a closure. Now we only do is it has a non-empty set of outlives requirements. --- .../borrow_check/nll/region_infer/mod.rs | 173 +++++++++++---------- .../escape-argument-callee.stderr | 3 +- .../closure-requirements/escape-argument.stderr | 3 +- .../propagate-approximated-fail-no-postdom.stderr | 3 +- ...shorter-to-static-comparing-against-free.stderr | 3 +- .../propagate-approximated-to-empty.stderr | 45 ++++++ ...ate-fail-to-approximate-longer-no-bounds.stderr | 7 +- ...-fail-to-approximate-longer-wrong-bounds.stderr | 7 +- .../return-wrong-bound-region.stderr | 3 +- 9 files changed, 148 insertions(+), 99 deletions(-) create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr (limited to 'src') 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 6d76398b941..af88edc22ce 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -251,24 +251,88 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, ) -> Option { assert!(self.inferred_values.is_none(), "values already inferred"); - let tcx = infcx.tcx; - // Find the minimal regions that can solve the constraints. This is infallible. self.propagate_constraints(mir); - // Now, see whether any of the constraints were too strong. In - // particular, we want to check for a case where a universally - // quantified region exceeded its bounds. Consider: - // - // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - // - // In this case, returning `x` requires `&'a u32 <: &'b u32` - // and hence we establish (transitively) a constraint that - // `'a: 'b`. The `propagate_constraints` code above will - // therefore add `end('a)` into the region for `'b` -- but we - // have no evidence that `'a` outlives `'b`, so we want to report - // an error. + let outlives_requirements = self.check_universal_regions(infcx, mir_def_id); + + if outlives_requirements.is_empty() { + None + } else { + let num_external_vids = self.universal_regions.num_global_and_external_regions(); + Some(ClosureRegionRequirements { + num_external_vids, + outlives_requirements, + }) + } + } + /// Propagate the region constraints: this will grow the values + /// for each region variable until all the constraints are + /// satisfied. Note that some values may grow **too** large to be + /// feasible, but we check this later. + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { + let mut changed = true; + + debug!("propagate_constraints()"); + debug!("propagate_constraints: constraints={:#?}", { + let mut constraints: Vec<_> = self.constraints.iter().collect(); + constraints.sort(); + constraints + }); + + // The initial values for each region are derived from the liveness + // constraints we have accumulated. + let mut inferred_values = self.liveness_constraints.clone(); + + while changed { + changed = false; + debug!("propagate_constraints: --------------------"); + for constraint in &self.constraints { + debug!("propagate_constraints: constraint={:?}", constraint); + + // Grow the value as needed to accommodate the + // outlives constraint. + let Ok(made_changes) = self.dfs( + mir, + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + }, + ); + + if made_changes { + debug!("propagate_constraints: sub={:?}", constraint.sub); + debug!("propagate_constraints: sup={:?}", constraint.sup); + changed = true; + } + } + debug!("\n"); + } + + self.inferred_values = Some(inferred_values); + } + + /// Once regions have been propagated, this method is used to see + /// whether any of the constraints were too strong. In particular, + /// we want to check for a case where a universally quantified + /// region exceeded its bounds. Consider: + /// + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// + /// In this case, returning `x` requires `&'a u32 <: &'b u32` + /// and hence we establish (transitively) a constraint that + /// `'a: 'b`. The `propagate_constraints` code above will + /// therefore add `end('a)` into the region for `'b` -- but we + /// have no evidence that `'b` outlives `'a`, so we want to report + /// an error. + fn check_universal_regions( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + mir_def_id: DefId, + ) -> Vec { // The universal regions are always found in a prefix of the // full list. let universal_definitions = self.definitions @@ -283,27 +347,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_universal_region(infcx, fr, &mut outlives_requirements); } - // If this is not a closure, then there is no caller to which we can - // "pass the buck". So if there are any outlives-requirements that were - // not satisfied, we just have to report a hard error here. - if !tcx.is_closure(mir_def_id) { - for outlives_requirement in outlives_requirements { - self.report_error( - infcx, - outlives_requirement.free_region, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); - } - return None; + // If this is a closure, we can propagate unsatisfied + // `outlives_requirements` to our creator. Otherwise, we have + // to report a hard error here. + if infcx.tcx.is_closure(mir_def_id) { + return outlives_requirements; } - let num_external_vids = self.universal_regions.num_global_and_external_regions(); + for outlives_requirement in outlives_requirements { + self.report_error( + infcx, + outlives_requirement.free_region, + outlives_requirement.outlived_free_region, + outlives_requirement.blame_span, + ); + } - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements, - }) + vec![] } /// Check the final value for the free region `fr` to see if it @@ -396,54 +456,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } - /// Propagate the region constraints: this will grow the values - /// for each region variable until all the constraints are - /// satisfied. Note that some values may grow **too** large to be - /// feasible, but we check this later. - fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { - let mut changed = true; - - debug!("propagate_constraints()"); - debug!("propagate_constraints: constraints={:#?}", { - let mut constraints: Vec<_> = self.constraints.iter().collect(); - constraints.sort(); - constraints - }); - - // The initial values for each region are derived from the liveness - // constraints we have accumulated. - let mut inferred_values = self.liveness_constraints.clone(); - - while changed { - changed = false; - debug!("propagate_constraints: --------------------"); - for constraint in &self.constraints { - debug!("propagate_constraints: constraint={:?}", constraint); - - // Grow the value as needed to accommodate the - // outlives constraint. - let Ok(made_changes) = self.dfs( - mir, - CopyFromSourceToTarget { - source_region: constraint.sub, - target_region: constraint.sup, - inferred_values: &mut inferred_values, - constraint_point: constraint.point, - }, - ); - - if made_changes { - debug!("propagate_constraints: sub={:?}", constraint.sub); - debug!("propagate_constraints: sup={:?}", constraint.sup); - changed = true; - } - } - debug!("\n"); - } - - self.inferred_values = Some(inferred_values); - } - /// Tries to finds a good span to blame for the fact that `fr1` /// contains `fr2`. fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { @@ -589,4 +601,3 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { } } } - diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index c842d51a2ad..2dfafd8f172 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -10,7 +10,7 @@ error: free region `'_#4r` does not outlive free region `'_#3r` 36 | let mut closure = expect_sig(|p, y| *p = y); | ^^^^^^ -note: External requirements +note: No external requirements --> $DIR/escape-argument-callee.rs:36:38 | 36 | let mut closure = expect_sig(|p, y| *p = y); @@ -20,7 +20,6 @@ note: External requirements i16, for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) i32)) ] - = note: number of external vids: 1 note: No external requirements --> $DIR/escape-argument-callee.rs:30:1 diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 0ec671997e7..567ed299ac1 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -1,4 +1,4 @@ -note: External requirements +note: No external requirements --> $DIR/escape-argument.rs:36:38 | 36 | let mut closure = expect_sig(|p, y| *p = y); @@ -8,7 +8,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) ] - = note: number of external vids: 1 note: No external requirements --> $DIR/escape-argument.rs:30:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index d581622c4c6..cdda8ab5392 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -10,7 +10,7 @@ error: free region `'_#5r` does not outlive free region `'_#6r` 57 | demand_y(x, y, p) | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9 | 53 | / |_outlives1, _outlives2, _outlives3, x, y| { @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) ] - = note: number of external vids: 4 note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:48:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index b93c69dc13f..3e54e62d011 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -10,7 +10,7 @@ error: free region `'_#2r` does not outlive free region `'_#1r` 33 | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure | ^^^^^^ -note: External requirements +note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15 | 31 | foo(cell, |cell_a, cell_x| { @@ -25,7 +25,6 @@ note: External requirements i32, for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>)) ] - = note: number of external vids: 2 note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:28:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr new file mode 100644 index 00000000000..e8dc8a13f87 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -0,0 +1,45 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-approximated-to-empty.rs:41:9 + | +41 | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `'_#6r` does not outlive free region `'_#4r` + --> $DIR/propagate-approximated-to-empty.rs:41:21 + | +41 | demand_y(x, y, x.get()) + | ^ + +note: No external requirements + --> $DIR/propagate-approximated-to-empty.rs:39:47 + | +39 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | _______________________________________________^ +40 | | // Only works if 'x: 'y: +41 | | demand_y(x, y, x.get()) +42 | | //~^ WARN not reporting region error due to -Znll +43 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r` +44 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[317d]::supply[0]::{{closure}}[0]) with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) + ] + +note: No external requirements + --> $DIR/propagate-approximated-to-empty.rs:38:1 + | +38 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +39 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +40 | | // Only works if 'x: 'y: +41 | | demand_y(x, y, x.get()) +... | +44 | | }); +45 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_approximated_to_empty[317d]::supply[0]) with substs [] + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 6094f9aad81..f3c40c838fb 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#6r` does not outlive free region `'_#4r` - --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21 + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:18 | 47 | demand_y(x, y, x.get()) - | ^ + | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47 | 45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) ] - = note: number of external vids: 2 note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:44:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 6658ee63abd..a66c2a78970 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#5r` does not outlive free region `'_#7r` - --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21 + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:18 | 51 | demand_y(x, y, x.get()) - | ^ + | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47 | 49 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>)) ] - = note: number of external vids: 3 note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:48:1 diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index 8999f69e8de..cb2b2e2f118 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -10,7 +10,7 @@ error: free region `'_#3r` does not outlive free region `'_#2r` 21 | expect_sig(|a, b| b); // ought to return `a` | ^ -note: External requirements +note: No external requirements --> $DIR/return-wrong-bound-region.rs:21:16 | 21 | expect_sig(|a, b| b); // ought to return `a` @@ -20,7 +20,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32 ] - = note: number of external vids: 1 note: No external requirements --> $DIR/return-wrong-bound-region.rs:20:1 -- cgit 1.4.1-3-g733a5 From 6193c5cc2acd1c427a372dfdab3d071e880ecf62 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 07:01:09 -0500 Subject: translate `Verify`s into `TypeTest`s and check them --- .../borrow_check/nll/region_infer/dfs.rs | 48 +++-- .../borrow_check/nll/region_infer/mod.rs | 220 ++++++++++++++++++++- .../nll/subtype_constraint_generation.rs | 68 ++++++- src/test/ui/nll/ty-outlives/ty-param-fn.rs | 51 +++++ src/test/ui/nll/ty-outlives/ty-param-fn.stderr | 26 +++ 5 files changed, 389 insertions(+), 24 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn.stderr (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs index c8ac0fa233f..dcf5d979a4c 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -11,11 +11,12 @@ //! Module defining the `dfs` method on `RegionInferenceContext`, along with //! its associated helper traits. +use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValues, RegionValueElements}; use rustc::mir::{Location, Mir}; use rustc::ty::RegionVid; use rustc_data_structures::fx::FxHashSet; -use super::RegionInferenceContext; -use super::values::{RegionElementIndex, RegionValues, RegionValueElements}; impl<'tcx> RegionInferenceContext<'tcx> { /// Function used to satisfy or test a `R1: R2 @ P` @@ -165,17 +166,16 @@ impl<'v> DfsOp for CopyFromSourceToTarget<'v> { /// condition. Similarly, if we reach the end of the graph and find /// that R1 contains some universal region that R2 does not contain, /// we abort the walk early. -#[allow(dead_code)] // TODO -pub(super) struct TestTarget<'v> { - source_region: RegionVid, - target_region: RegionVid, - elements: &'v RegionValueElements, - inferred_values: &'v RegionValues, - constraint_point: Location, +pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> { + pub source_region: RegionVid, + pub target_region: RegionVid, + pub elements: &'v RegionValueElements, + pub universal_regions: &'v UniversalRegions<'tcx>, + pub inferred_values: &'v RegionValues, + pub constraint_point: Location, } -#[allow(dead_code)] // TODO -impl<'v> DfsOp for TestTarget<'v> { +impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { /// The element that was not found within R2. type Early = RegionElementIndex; @@ -204,12 +204,32 @@ impl<'v> DfsOp for TestTarget<'v> { fn add_universal_regions_outlived_by_source_to_target( &mut self, ) -> Result { - for ur in self.inferred_values + // For all `ur_in_source` in `source_region`. + for ur_in_source in self.inferred_values .universal_regions_outlived_by(self.source_region) { - if !self.inferred_values.contains(self.target_region, ur) { - return Err(self.elements.index(ur)); + // Check that `target_region` outlives `ur_in_source`. + + // If `ur_in_source` is a member of `target_region`, OK. + // + // (This is implied by the loop below, actually, just an + // irresistible micro-opt. Mm. Premature optimization. So + // tasty.) + if self.inferred_values.contains(self.target_region, ur_in_source) { + continue; } + + // If there is some other element X such that `target_region: X` and + // `X: ur_in_source`, OK. + if self.inferred_values + .universal_regions_outlived_by(self.target_region) + .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source)) + { + continue; + } + + // Otherwise, not known to be true. + return Err(self.elements.index(ur_in_source)); } Ok(false) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index af88edc22ce..86ba7524bf0 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -14,7 +14,7 @@ use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; -use rustc::infer::region_constraints::VarOrigins; +use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; @@ -24,7 +24,7 @@ use syntax_pos::Span; mod annotation; mod dfs; -use self::dfs::CopyFromSourceToTarget; +use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource}; mod dump_mir; mod graphviz; mod values; @@ -53,6 +53,9 @@ pub struct RegionInferenceContext<'tcx> { /// The constraints we have accumulated and used during solving. constraints: Vec, + /// Type constraints that we check after solving. + type_tests: Vec>, + /// Information about the universally quantified regions in scope /// on this function and their (known) relations to one another. universal_regions: UniversalRegions<'tcx>, @@ -95,6 +98,90 @@ pub struct Constraint { span: Span, } +/// A "type test" corresponds to an outlives constraint between a type +/// and a lifetime, like `T: 'x` or `::Bar: 'x`. They are +/// translated from the `Verify` region constraints in the ordinary +/// inference context. +/// +/// These sorts of constraints are handled differently than ordinary +/// constraints, at least at present. During type checking, the +/// `InferCtxt::process_registered_region_obligations` method will +/// attempt to convert a type test like `T: 'x` into an ordinary +/// outlives constraint when possible (for example, `&'a T: 'b` will +/// be converted into `'a: 'b` and registered as a `Constraint`). +/// +/// In some cases, however, there are outlives relationships that are +/// not converted into a region constraint, but rather into one of +/// these "type tests". The distinction is that a type test does not +/// influence the inference result, but instead just examines the +/// values that we ultimately inferred for each region variable and +/// checks that they meet certain extra criteria. If not, an error +/// can be issued. +/// +/// One reason for this is that these type tests always boil down to a +/// check like `'a: 'x` where `'a` is a universally quantified region +/// -- and therefore not one whose value is really meant to be +/// *inferred*, precisely. Another reason is that these type tests can +/// involve *disjunction* -- that is, they can be satisfied in more +/// than one way. +/// +/// For more information about this translation, see +/// `InferCtxt::process_registered_region_obligations` and +/// `InferCtxt::type_must_outlive` in `rustc::infer::outlives`. +#[derive(Clone, Debug)] +pub struct TypeTest<'tcx> { + /// The type `T` that must outlive the region. + pub generic_kind: GenericKind<'tcx>, + + /// The region `'x` that the type must outlive. + pub lower_bound: RegionVid, + + /// The point where the outlives relation must hold. + pub point: Location, + + /// Where did this constraint arise? + pub span: Span, + + /// A test which, if met by the region `'x`, proves that this type + /// constraint is satisfied. + pub test: RegionTest, +} + +/// A "test" that can be applied to some "subject region" `'x`. These are used to +/// describe type constraints. Tests do not presently affect the +/// region values that get inferred for each variable; they only +/// examine the results *after* inference. This means they can +/// conveniently include disjuction ("a or b must be true"). +#[derive(Clone, Debug)] +pub enum RegionTest { + /// The subject region `'x` must by outlived by *some* region in + /// the given set of regions. + /// + /// This test comes from e.g. a where clause like `T: 'a + 'b`, + /// which implies that we know that `T: 'a` and that `T: + /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can + /// do so by showing that `'a: 'x` *or* `'b: 'x`. + IsOutlivedByAnyRegionIn(Vec), + + /// The subject region `'x` must by outlived by *all* regions in + /// the given set of regions. + /// + /// This test comes from e.g. a projection type like `T = >::Foo`, which must outlive `'a` or `'b`, and + /// maybe both. Therefore we can prove that `T: 'x` if we know + /// that `'a: 'x` *and* `'b: 'x`. + IsOutlivedByAllRegionsIn(Vec), + + /// Any of the given tests are true. + /// + /// This arises from projections, for which there are multiple + /// ways to prove an outlives relationship. + Any(Vec), + + /// All of the given tests are true. + All(Vec), +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -122,6 +209,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { liveness_constraints: RegionValues::new(elements, num_region_variables), inferred_values: None, constraints: Vec::new(), + type_tests: Vec::new(), universal_regions, }; @@ -243,7 +331,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); } - /// Perform region inference. + /// Add a "type test" that must be satisfied. + pub(super) fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { + self.type_tests.push(type_test); + } + + /// Perform region inference and report errors if we see any + /// unsatisfiable constraints. If this is a closure, returns the + /// region requirements to propagate to our creator, if any. pub(super) fn solve( &mut self, infcx: &InferCtxt<'_, '_, 'tcx>, @@ -254,6 +349,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.propagate_constraints(mir); + self.check_type_tests(infcx, mir); + let outlives_requirements = self.check_universal_regions(infcx, mir_def_id); if outlives_requirements.is_empty() { @@ -315,6 +412,123 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } + /// Once regions have been propagated, this method is used to see + /// whether any of the constraints were too strong. In particular, + /// we want to check for a case where a universally quantified + /// region exceeded its bounds. Consider: + /// + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// + /// In this case, returning `x` requires `&'a u32 <: &'b u32` + /// and hence we establish (transitively) a constraint that + /// `'a: 'b`. The `propagate_constraints` code above will + /// therefore add `end('a)` into the region for `'b` -- but we + /// have no evidence that `'b` outlives `'a`, so we want to report + /// an error. + fn check_type_tests( + &self, + infcx: &InferCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + ) { + for type_test in &self.type_tests { + debug!("check_type_test: {:?}", type_test); + + if !self.eval_region_test( + mir, + type_test.point, + type_test.lower_bound, + &type_test.test, + ) { + // Oh the humanity. Obviously we will do better than this error eventually. + infcx.tcx.sess.span_err( + type_test.span, + &format!("failed type test: {:?}", type_test), + ); + } + } + } + + /// Test if `test` is true when applied to `lower_bound` at + /// `point`, and returns true or false. + fn eval_region_test( + &self, + mir: &Mir<'tcx>, + point: Location, + lower_bound: RegionVid, + test: &RegionTest, + ) -> bool { + debug!( + "eval_region_test(point={:?}, lower_bound={:?}, test={:?})", + point, + lower_bound, + test + ); + + match test { + RegionTest::IsOutlivedByAllRegionsIn(regions) => regions + .iter() + .all(|&r| self.eval_outlives(mir, r, lower_bound, point)), + + RegionTest::IsOutlivedByAnyRegionIn(regions) => regions + .iter() + .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), + + RegionTest::Any(tests) => tests + .iter() + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), + + RegionTest::All(tests) => tests + .iter() + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), + } + } + + // Evaluate whether `sup_region: sub_region @ point`. + fn eval_outlives( + &self, + mir: &Mir<'tcx>, + sup_region: RegionVid, + sub_region: RegionVid, + point: Location, + ) -> bool { + debug!( + "eval_outlives({:?}: {:?} @ {:?})", + sup_region, + sub_region, + point + ); + + // Roughly speaking, do a DFS of all region elements reachable + // from `point` contained in `sub_region`. If any of those are + // *not* present in `sup_region`, the DFS will abort early and + // yield an `Err` result. + match self.dfs( + mir, + TestTargetOutlivesSource { + source_region: sub_region, + target_region: sup_region, + constraint_point: point, + elements: &self.elements, + universal_regions: &self.universal_regions, + inferred_values: self.inferred_values.as_ref().unwrap(), + }, + ) { + Ok(_) => { + debug!("eval_outlives: true"); + true + } + + Err(elem) => { + debug!( + "eval_outlives: false because `{:?}` is not present in `{:?}`", + self.elements.to_element(elem), + sup_region + ); + false + } + } + } + /// Once regions have been propagated, this method is used to see /// whether any of the constraints were too strong. In particular, /// we want to check for a case where a universally quantified diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index c98a94fa8bc..73c40827c54 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -11,11 +11,14 @@ use rustc::mir::Mir; use rustc::infer::region_constraints::Constraint; use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::region_constraints::{Verify, VerifyBound}; use rustc::ty; +use syntax::codemap::Span; +use transform::type_check::Locations; use transform::type_check::MirTypeckRegionConstraints; use transform::type_check::OutlivesSet; -use super::region_infer::RegionInferenceContext; +use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest}; /// When the MIR type-checker executes, it validates all the types in /// the MIR, and in the process generates a set of constraints that @@ -27,10 +30,7 @@ pub(super) fn generate<'tcx>( mir: &Mir<'tcx>, constraints: &MirTypeckRegionConstraints<'tcx>, ) { - SubtypeConstraintGenerator { - regioncx, - mir, - }.generate(constraints); + SubtypeConstraintGenerator { regioncx, mir }.generate(constraints); } struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { @@ -65,6 +65,8 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { givens, } = data; + let span = self.mir.source_info(locations.from_location).span; + for constraint in constraints.keys() { debug!("generate: constraint: {:?}", constraint); let (a_vid, b_vid) = match constraint { @@ -81,12 +83,15 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { // reverse direction, because `regioncx` talks about // "outlives" (`>=`) whereas the region constraints // talk about `<=`. - let span = self.mir.source_info(locations.from_location).span; self.regioncx .add_outlives(span, b_vid, a_vid, locations.at_location); } - assert!(verifys.is_empty(), "verifys not yet implemented"); + for verify in verifys { + let type_test = self.verify_to_type_test(verify, span, locations); + self.regioncx.add_type_test(type_test); + } + assert!( givens.is_empty(), "MIR type-checker does not use givens (thank goodness)" @@ -94,6 +99,55 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { } } + fn verify_to_type_test( + &self, + verify: &Verify<'tcx>, + span: Span, + locations: &Locations, + ) -> TypeTest<'tcx> { + let generic_kind = verify.kind; + + let lower_bound = self.to_region_vid(verify.region); + + let point = locations.at_location; + + let test = self.verify_bound_to_region_test(&verify.bound); + + TypeTest { + generic_kind, + lower_bound, + point, + span, + test, + } + } + + fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { + match verify_bound { + VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AnyBound(bounds) => RegionTest::Any( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + + VerifyBound::AllBounds(bounds) => RegionTest::All( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + } + } + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { // Every region that we see in the constraints came from the // MIR or from the parameter environment. If the former, it diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs new file mode 100644 index 00000000000..c6547ae68fa --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -0,0 +1,51 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box) -> Box +where + T: Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr new file mode 100644 index 00000000000..5b29ff88621 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn.rs:22:5 + | +22 | x + | ^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn.rs:38:5 + | +38 | x + | ^ + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#2r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn([]) } + --> $DIR/ty-param-fn.rs:22:5 + | +22 | x + | ^ + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-fn.rs:38:5 + | +38 | x + | ^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From fad3d1d7fd16fea674bdafe49649fb52e15ab118 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Dec 2017 16:57:43 -0500 Subject: dfs.rs: rustfmt --- .../borrow_check/nll/region_infer/dfs.rs | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs index dcf5d979a4c..59860d61ab9 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -13,7 +13,8 @@ use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::region_infer::RegionInferenceContext; -use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValues, RegionValueElements}; +use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements, + RegionValues}; use rustc::mir::{Location, Mir}; use rustc::ty::RegionVid; use rustc_data_structures::fx::FxHashSet; @@ -112,9 +113,7 @@ pub(super) trait DfsOp { /// Adds all universal regions in the source region to the target region, returning /// true if something has changed. - fn add_universal_regions_outlived_by_source_to_target( - &mut self, - ) -> Result; + fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result; } /// Used during inference to enforce a `R1: R2 @ P` constraint. For @@ -143,16 +142,11 @@ impl<'v> DfsOp for CopyFromSourceToTarget<'v> { .contains(self.source_region, point_index) } - fn add_to_target_region( - &mut self, - point_index: RegionElementIndex, - ) -> Result { + fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result { Ok(self.inferred_values.add(self.target_region, point_index)) } - fn add_universal_regions_outlived_by_source_to_target( - &mut self, - ) -> Result { + fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result { Ok( self.inferred_values .add_universal_regions_outlived_by(self.source_region, self.target_region), @@ -215,16 +209,19 @@ impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { // (This is implied by the loop below, actually, just an // irresistible micro-opt. Mm. Premature optimization. So // tasty.) - if self.inferred_values.contains(self.target_region, ur_in_source) { + if self.inferred_values + .contains(self.target_region, ur_in_source) + { continue; } // If there is some other element X such that `target_region: X` and // `X: ur_in_source`, OK. if self.inferred_values - .universal_regions_outlived_by(self.target_region) - .any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source)) - { + .universal_regions_outlived_by(self.target_region) + .any(|ur_in_target| { + self.universal_regions.outlives(ur_in_target, ur_in_source) + }) { continue; } -- cgit 1.4.1-3-g733a5 From 1c57468840f708e52db9b1e59b536c4f4783e823 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 12:03:44 -0500 Subject: move `type_check` out of `transform` and into the `nll` module --- src/librustc_mir/borrow_check/nll/mod.rs | 8 +- .../nll/subtype_constraint_generation.rs | 6 +- .../borrow_check/nll/type_check/mod.rs | 1517 ++++++++++++++++++++ src/librustc_mir/transform/mod.rs | 2 +- src/librustc_mir/transform/type_check.rs | 1517 -------------------- 5 files changed, 1525 insertions(+), 1525 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/mod.rs delete mode 100644 src/librustc_mir/transform/type_check.rs (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 218142f7e2d..ffbb3d31917 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -16,7 +16,6 @@ use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; use std::io; use transform::MirSource; -use transform::type_check; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; use borrow_check::FlowAtLocation; use dataflow::MaybeInitializedLvals; @@ -27,14 +26,15 @@ use util::pretty::{self, ALIGN}; use self::mir_util::PassWhere; mod constraint_generation; +pub(crate) mod region_infer; +mod renumber; mod subtype_constraint_generation; +pub(crate) mod type_check; mod universal_regions; -use self::universal_regions::UniversalRegions; -pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; +use self::universal_regions::UniversalRegions; -mod renumber; /// Rewrites the regions in the MIR to use NLL variables, also /// scraping out the set of universal regions (e.g., region parameters) diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index 73c40827c54..e42302761bf 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -14,11 +14,11 @@ use rustc::infer::region_constraints::RegionConstraintData; use rustc::infer::region_constraints::{Verify, VerifyBound}; use rustc::ty; use syntax::codemap::Span; -use transform::type_check::Locations; -use transform::type_check::MirTypeckRegionConstraints; -use transform::type_check::OutlivesSet; use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest}; +use super::type_check::Locations; +use super::type_check::MirTypeckRegionConstraints; +use super::type_check::OutlivesSet; /// When the MIR type-checker executes, it validates all the types in /// the MIR, and in the process generates a set of constraints that diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs new file mode 100644 index 00000000000..fae911780a1 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -0,0 +1,1517 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This pass type-checks the MIR to ensure it is not broken. +#![allow(unreachable_code)] + +use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; +use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::traits::{self, FulfillmentContext}; +use rustc::ty::error::TypeError; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; +use rustc::middle::const_val::ConstVal; +use rustc::mir::*; +use rustc::mir::tcx::PlaceTy; +use rustc::mir::visit::{PlaceContext, Visitor}; +use std::fmt; +use syntax::ast; +use syntax_pos::{Span, DUMMY_SP}; +use transform::{MirPass, MirSource}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::indexed_vec::Idx; + +/// Type checks the given `mir` in the context of the inference +/// context `infcx`. Returns any region constraints that have yet to +/// be proven. +/// +/// This phase of type-check ought to be infallible -- this is because +/// the original, HIR-based type-check succeeded. So if any errors +/// occur here, we will get a `bug!` reported. +pub fn type_check<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + let mut checker = TypeChecker::new(infcx, body_id, param_env); + let errors_reported = { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + verifier.errors_reported + }; + + if !errors_reported { + // if verifier failed, don't do further checks to avoid ICEs + checker.typeck_mir(mir); + } + + checker.constraints +} + +fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { + // We sometimes see MIR failures (notably predicate failures) due to + // the fact that we check rvalue sized predicates here. So use `delay_span_bug` + // to avoid reporting bugs in those cases. + tcx.sess.diagnostic().delay_span_bug(span, msg); +} + +macro_rules! span_mirbug { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + mirbug($context.tcx(), $context.last_span, + &format!("broken MIR in {:?} ({:?}): {}", + $context.body_id, + $elem, + format_args!($($message)*))) + }) +} + +macro_rules! span_mirbug_and_err { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + { + span_mirbug!($context, $elem, $($message)*); + $context.error() + } + }) +} + +enum FieldAccessError { + OutOfRange { field_count: usize }, +} + +/// Verifies that MIR types are sane to not crash further checks. +/// +/// The sanitize_XYZ methods here take an MIR object and compute its +/// type, calling `span_mirbug` and returning an error type if there +/// is a problem. +struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> { + cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, + mir: &'a Mir<'tcx>, + last_span: Span, + body_id: ast::NodeId, + errors_reported: bool, +} + +impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { + fn visit_span(&mut self, span: &Span) { + if *span != DUMMY_SP { + self.last_span = *span; + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.sanitize_place(place, location, context); + } + + fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { + self.super_constant(constant, location); + self.sanitize_constant(constant, location); + self.sanitize_type(constant, constant.ty); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + let rval_ty = rvalue.ty(self.mir, self.tcx()); + self.sanitize_type(rvalue, rval_ty); + } + + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + self.super_local_decl(local, local_decl); + self.sanitize_type(local_decl, local_decl.ty); + } + + fn visit_mir(&mut self, mir: &Mir<'tcx>) { + self.sanitize_type(&"return type", mir.return_ty()); + for local_decl in &mir.local_decls { + self.sanitize_type(local_decl, local_decl.ty); + } + if self.errors_reported { + return; + } + self.super_mir(mir); + } +} + +impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { + fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + TypeVerifier { + mir, + body_id: cx.body_id, + cx, + last_span: mir.span, + errors_reported: false, + } + } + + fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { + self.cx.infcx.tcx + } + + fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty.has_escaping_regions() || ty.references_error() { + span_mirbug_and_err!(self, parent, "bad type {:?}", ty) + } else { + ty + } + } + + /// Checks that the constant's `ty` field matches up with what + /// would be expected from its literal. + fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) { + debug!( + "sanitize_constant(constant={:?}, location={:?})", + constant, + location + ); + + let expected_ty = match constant.literal { + Literal::Value { value } => { + // FIXME(#46702) -- We need some way to get the predicates + // associated with the "pre-evaluated" form of the + // constant. For example, consider that the constant + // may have associated constant projections (`>::SOME_CONST`) that impose + // constraints on `'a` and `'b`. These constraints + // would be lost if we just look at the normalized + // value. + if let ConstVal::Function(def_id, ..) = value.val { + let tcx = self.tcx(); + let type_checker = &mut self.cx; + + // FIXME -- For now, use the substitutions from + // `value.ty` rather than `value.val`. The + // renumberer will rewrite them to independent + // sets of regions; in principle, we ought to + // derive the type of the `value.val` from "first + // principles" and equate with value.ty, but as we + // are transitioning to the miri-based system, we + // don't have a handy function for that, so for + // now we just ignore `value.val` regions. + let substs = match value.ty.sty { + ty::TyFnDef(ty_def_id, substs) => { + assert_eq!(def_id, ty_def_id); + substs + } + _ => span_bug!( + self.last_span, + "unexpected type for constant function: {:?}", + value.ty + ), + }; + + let instantiated_predicates = + tcx.predicates_of(def_id).instantiate(tcx, substs); + let predicates = + type_checker.normalize(&instantiated_predicates.predicates, location); + type_checker.prove_predicates(&predicates, location); + } + + value.ty + } + + Literal::Promoted { .. } => { + // FIXME -- promoted MIR return types reference + // various "free regions" (e.g., scopes and things) + // that they ought not to do. We have to figure out + // how best to handle that -- probably we want treat + // promoted MIR much like closures, renumbering all + // their free regions and propagating constraints + // upwards. We have the same acyclic guarantees, so + // that should be possible. But for now, ignore them. + // + // let promoted_mir = &self.mir.promoted[index]; + // promoted_mir.return_ty() + return; + } + }; + + debug!("sanitize_constant: expected_ty={:?}", expected_ty); + + if let Err(terr) = self.cx + .eq_types(expected_ty, constant.ty, location.at_self()) + { + span_mirbug!( + self, + constant, + "constant {:?} should have type {:?} but has {:?} ({:?})", + constant, + expected_ty, + constant.ty, + terr, + ); + } + } + + /// Checks that the types internal to the `place` match up with + /// what would be expected. + fn sanitize_place( + &mut self, + place: &Place<'tcx>, + location: Location, + context: PlaceContext, + ) -> PlaceTy<'tcx> { + debug!("sanitize_place: {:?}", place); + let place_ty = match *place { + Place::Local(index) => PlaceTy::Ty { + ty: self.mir.local_decls[index].ty, + }, + Place::Static(box Static { def_id, ty: sty }) => { + let sty = self.sanitize_type(place, sty); + let ty = self.tcx().type_of(def_id); + let ty = self.cx.normalize(&ty, location); + if let Err(terr) = self.cx.eq_types(ty, sty, location.at_self()) { + span_mirbug!( + self, + place, + "bad static type ({:?}: {:?}): {:?}", + ty, + sty, + terr + ); + } + PlaceTy::Ty { ty: sty } + } + Place::Projection(ref proj) => { + let base_context = if context.is_mutating_use() { + PlaceContext::Projection(Mutability::Mut) + } else { + PlaceContext::Projection(Mutability::Not) + }; + let base_ty = self.sanitize_place(&proj.base, location, base_context); + if let PlaceTy::Ty { ty } = base_ty { + if ty.references_error() { + assert!(self.errors_reported); + return PlaceTy::Ty { + ty: self.tcx().types.err, + }; + } + } + self.sanitize_projection(base_ty, &proj.elem, place, location) + } + }; + if let PlaceContext::Copy = context { + let ty = place_ty.to_ty(self.tcx()); + if self.cx + .infcx + .type_moves_by_default(self.cx.param_env, ty, DUMMY_SP) + { + span_mirbug!(self, place, "attempted copy of non-Copy type ({:?})", ty); + } + } + place_ty + } + + fn sanitize_projection( + &mut self, + base: PlaceTy<'tcx>, + pi: &PlaceElem<'tcx>, + place: &Place<'tcx>, + location: Location, + ) -> PlaceTy<'tcx> { + debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); + let tcx = self.tcx(); + let base_ty = base.to_ty(tcx); + match *pi { + ProjectionElem::Deref => { + let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); + PlaceTy::Ty { + ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { + span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty) + }), + } + } + ProjectionElem::Index(i) => { + let index_ty = Place::Local(i).ty(self.mir, tcx).to_ty(tcx); + if index_ty != tcx.types.usize { + PlaceTy::Ty { + ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i), + } + } else { + PlaceTy::Ty { + ty: base_ty.builtin_index().unwrap_or_else(|| { + span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) + }), + } + } + } + ProjectionElem::ConstantIndex { .. } => { + // consider verifying in-bounds + PlaceTy::Ty { + ty: base_ty.builtin_index().unwrap_or_else(|| { + span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) + }), + } + } + ProjectionElem::Subslice { from, to } => PlaceTy::Ty { + ty: match base_ty.sty { + ty::TyArray(inner, size) => { + let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let min_size = (from as u64) + (to as u64); + if let Some(rest_size) = size.checked_sub(min_size) { + tcx.mk_array(inner, rest_size) + } else { + span_mirbug_and_err!( + self, + place, + "taking too-small slice of {:?}", + base_ty + ) + } + } + ty::TySlice(..) => base_ty, + _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), + }, + }, + ProjectionElem::Downcast(adt_def1, index) => match base_ty.sty { + ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { + if index >= adt_def.variants.len() { + PlaceTy::Ty { + ty: span_mirbug_and_err!( + self, + place, + "cast to variant #{:?} but enum only has {:?}", + index, + adt_def.variants.len() + ), + } + } else { + PlaceTy::Downcast { + adt_def, + substs, + variant_index: index, + } + } + } + _ => PlaceTy::Ty { + ty: span_mirbug_and_err!( + self, + place, + "can't downcast {:?} as {:?}", + base_ty, + adt_def1 + ), + }, + }, + ProjectionElem::Field(field, fty) => { + let fty = self.sanitize_type(place, fty); + match self.field_ty(place, base, field, location) { + Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) { + span_mirbug!( + self, + place, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); + }, + Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( + self, + place, + "accessed field #{} but variant only has {}", + field.index(), + field_count + ), + } + PlaceTy::Ty { ty: fty } + } + } + } + + fn error(&mut self) -> Ty<'tcx> { + self.errors_reported = true; + self.tcx().types.err + } + + fn field_ty( + &mut self, + parent: &fmt::Debug, + base_ty: PlaceTy<'tcx>, + field: Field, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + let (variant, substs) = match base_ty { + PlaceTy::Downcast { + adt_def, + substs, + variant_index, + } => (&adt_def.variants[variant_index], substs), + PlaceTy::Ty { ty } => match ty.sty { + ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => (&adt_def.variants[0], substs), + ty::TyClosure(def_id, substs) => { + return match substs.upvar_tys(def_id, tcx).nth(field.index()) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.upvar_tys(def_id, tcx).count(), + }), + } + } + ty::TyGenerator(def_id, substs, _) => { + // Try upvars first. `field_tys` requires final optimized MIR. + if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field.index()) { + return Ok(ty); + } + + return match substs.field_tys(def_id, tcx).nth(field.index()) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.field_tys(def_id, tcx).count() + 1, + }), + }; + } + ty::TyTuple(tys, _) => { + return match tys.get(field.index()) { + Some(&ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: tys.len(), + }), + } + } + _ => { + return Ok(span_mirbug_and_err!( + self, + parent, + "can't project out of {:?}", + base_ty + )) + } + }, + }; + + if let Some(field) = variant.fields.get(field.index()) { + Ok(self.cx.normalize(&field.ty(tcx, substs), location)) + } else { + Err(FieldAccessError::OutOfRange { + field_count: variant.fields.len(), + }) + } + } +} + +/// The MIR type checker. Visits the MIR and enforces all the +/// constraints needed for it to be valid and well-typed. Along the +/// way, it accrues region constraints -- these can later be used by +/// NLL region checking. +pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'gcx>, + last_span: Span, + body_id: ast::NodeId, + reported_errors: FxHashSet<(Ty<'tcx>, Span)>, + constraints: MirTypeckRegionConstraints<'tcx>, +} + +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +#[derive(Default)] +pub struct MirTypeckRegionConstraints<'tcx> { + /// In general, the type-checker is not responsible for enforcing + /// liveness constraints; this job falls to the region inferencer, + /// which performs a liveness analysis. However, in some limited + /// cases, the MIR type-checker creates temporary regions that do + /// 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. + pub liveness_set: Vec<(ty::Region<'tcx>, Location)>, + + /// During the course of type-checking, we will accumulate region + /// constraints due to performing subtyping operations or solving + /// traits. These are accumulated into this vector for later use. + pub outlives_sets: Vec>, +} + +/// Outlives relationships between regions and types created at a +/// particular point within the control-flow graph. +pub struct OutlivesSet<'tcx> { + /// The locations associated with these constraints. + pub locations: Locations, + + /// Constraints generated. In terms of the NLL RFC, when you have + /// a constraint `R1: R2 @ P`, the data in there specifies things + /// like `R1: R2`. + pub data: RegionConstraintData<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +pub struct Locations { + /// The location in the MIR that generated these constraints. + /// This is intended for error reporting and diagnosis; the + /// constraints may *take effect* at a distinct spot. + pub from_location: Location, + + /// The constraints must be met at this location. In terms of the + /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field + /// is the `P` value. + pub at_location: Location, +} + +impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { + fn new( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + ) -> Self { + TypeChecker { + infcx, + last_span: DUMMY_SP, + body_id, + param_env, + reported_errors: FxHashSet(), + constraints: MirTypeckRegionConstraints::default(), + } + } + + fn misc(&self, span: Span) -> traits::ObligationCause<'tcx> { + traits::ObligationCause::misc(span, self.body_id) + } + + fn fully_perform_op( + &mut self, + locations: Locations, + op: OP, + ) -> Result> + where + OP: FnOnce(&mut Self) -> InferResult<'tcx, R>, + { + let mut fulfill_cx = FulfillmentContext::new(); + let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; + fulfill_cx.register_predicate_obligations(self.infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { + span_mirbug!(self, "", "errors selecting obligation: {:?}", e); + } + + self.infcx + .process_registered_region_obligations(&[], None, self.param_env, self.body_id); + + let data = self.infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + self.constraints + .outlives_sets + .push(OutlivesSet { locations, data }); + } + + Ok(value) + } + + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + ) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) + .sup(sup, sub) + }) + } + + fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) + .eq(b, a) + }) + } + + fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>, location: Location) { + debug!("check_stmt: {:?}", stmt); + let tcx = self.tcx(); + match stmt.kind { + StatementKind::Assign(ref place, ref rv) => { + let place_ty = place.ty(mir, tcx).to_ty(tcx); + let rv_ty = rv.ty(mir, tcx); + if let Err(terr) = + self.sub_types(rv_ty, place_ty, location.at_successor_within_block()) + { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } + self.check_rvalue(mir, rv, location); + } + StatementKind::SetDiscriminant { + ref place, + variant_index, + } => { + let place_type = place.ty(mir, tcx).to_ty(tcx); + let adt = match place_type.sty { + TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt, + _ => { + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): lhs is not an enum", + place, + variant_index + ); + } + }; + if variant_index >= adt.variants.len() { + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): value of of range", + place, + variant_index + ); + }; + } + StatementKind::StorageLive(_) | + StatementKind::StorageDead(_) | + StatementKind::InlineAsm { .. } | + StatementKind::EndRegion(_) | + StatementKind::Validate(..) | + StatementKind::Nop => {} + } + } + + fn check_terminator( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + term_location: Location, + ) { + debug!("check_terminator: {:?}", term); + let tcx = self.tcx(); + match term.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::GeneratorDrop | + TerminatorKind::Unreachable | + TerminatorKind::Drop { .. } | + TerminatorKind::FalseEdges { .. } => { + // no checks needed for these + } + + TerminatorKind::DropAndReplace { + ref location, + ref value, + target, + unwind, + } => { + let place_ty = location.ty(mir, tcx).to_ty(tcx); + let rv_ty = value.ty(mir, tcx); + + let locations = Locations { + from_location: term_location, + at_location: target.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } + + // Subtle: this assignment occurs at the start of + // *both* blocks, so we need to ensure that it holds + // at both locations. + if let Some(unwind) = unwind { + let locations = Locations { + from_location: term_location, + at_location: unwind.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } + } + } + TerminatorKind::SwitchInt { + ref discr, + switch_ty, + .. + } => { + let discr_ty = discr.ty(mir, tcx); + if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad SwitchInt ({:?} on {:?}): {:?}", + switch_ty, + discr_ty, + terr + ); + } + if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); + } + // FIXME: check the values + } + TerminatorKind::Call { + ref func, + ref args, + ref destination, + .. + } => { + let func_ty = func.ty(mir, tcx); + debug!("check_terminator: call, func_ty={:?}", func_ty); + let sig = match func_ty.sty { + ty::TyFnDef(..) | ty::TyFnPtr(_) => func_ty.fn_sig(tcx), + _ => { + span_mirbug!(self, term, "call to non-function {:?}", func_ty); + return; + } + }; + let (sig, map) = self.infcx.replace_late_bound_regions_with_fresh_var( + term.source_info.span, + LateBoundRegionConversionTime::FnCall, + &sig, + ); + let sig = self.normalize(&sig, term_location); + self.check_call_dest(mir, term, &sig, destination, term_location); + + // The ordinary liveness rules will ensure that all + // regions in the type of the callee are live here. We + // then further constrain the late-bound regions that + // were instantiated at the call site to be live as + // well. The resulting is that all the input (and + // output) types in the signature must be live, since + // all the inputs that fed into it were live. + for &late_bound_region in map.values() { + self.constraints + .liveness_set + .push((late_bound_region, term_location)); + } + + if self.is_box_free(func) { + self.check_box_free_inputs(mir, term, &sig, args, term_location); + } else { + self.check_call_inputs(mir, term, &sig, args, term_location); + } + } + TerminatorKind::Assert { + ref cond, ref msg, .. + } => { + let cond_ty = cond.ty(mir, tcx); + if cond_ty != tcx.types.bool { + span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); + } + + if let AssertMessage::BoundsCheck { ref len, ref index } = *msg { + if len.ty(mir, tcx) != tcx.types.usize { + span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) + } + if index.ty(mir, tcx) != tcx.types.usize { + span_mirbug!(self, index, "bounds-check index non-usize {:?}", index) + } + } + } + TerminatorKind::Yield { ref value, .. } => { + let value_ty = value.ty(mir, tcx); + match mir.yield_ty { + None => span_mirbug!(self, term, "yield in non-generator"), + Some(ty) => { + if let Err(terr) = self.sub_types(value_ty, ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "type of yield value is {:?}, but the yield type is {:?}: {:?}", + value_ty, + ty, + terr + ); + } + } + } + } + } + } + + fn check_call_dest( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: &Option<(Place<'tcx>, BasicBlock)>, + term_location: Location, + ) { + let tcx = self.tcx(); + match *destination { + Some((ref dest, target_block)) => { + let dest_ty = dest.ty(mir, tcx).to_ty(tcx); + let locations = Locations { + from_location: term_location, + at_location: target_block.start_location(), + }; + if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); + } + } + None => { + // FIXME(canndrew): This is_never should probably be an is_uninhabited + if !sig.output().is_never() { + span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); + } + } + } + } + + fn check_call_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + ) { + debug!("check_call_inputs({:?}, {:?})", sig, args); + if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) { + span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); + } + for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { + let op_arg_ty = op_arg.ty(mir, self.tcx()); + if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad arg #{:?} ({:?} <- {:?}): {:?}", + n, + fn_arg, + op_arg_ty, + terr + ); + } + } + } + + fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { + match operand { + &Operand::Constant(box Constant { + literal: + Literal::Value { + value: + &ty::Const { + val: ConstVal::Function(def_id, _), + .. + }, + .. + }, + .. + }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), + _ => false, + } + } + + fn check_box_free_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + ) { + debug!("check_box_free_inputs"); + + // box_free takes a Box as a pointer. Allow for that. + + if sig.inputs().len() != 1 { + span_mirbug!(self, term, "box_free should take 1 argument"); + return; + } + + let pointee_ty = match sig.inputs()[0].sty { + ty::TyRawPtr(mt) => mt.ty, + _ => { + span_mirbug!(self, term, "box_free should take a raw ptr"); + return; + } + }; + + if args.len() != 1 { + span_mirbug!(self, term, "box_free called with wrong # of args"); + return; + } + + let ty = args[0].ty(mir, self.tcx()); + let arg_ty = match ty.sty { + ty::TyRawPtr(mt) => mt.ty, + ty::TyAdt(def, _) if def.is_box() => ty.boxed_ty(), + _ => { + span_mirbug!(self, term, "box_free called with bad arg ty"); + return; + } + }; + + if let Err(terr) = self.sub_types(arg_ty, pointee_ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad box_free arg ({:?} <- {:?}): {:?}", + pointee_ty, + arg_ty, + terr + ); + } + } + + fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) { + let is_cleanup = block_data.is_cleanup; + self.last_span = block_data.terminator().source_info.span; + match block_data.terminator().kind { + TerminatorKind::Goto { target } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup) + } + TerminatorKind::SwitchInt { ref targets, .. } => for target in targets { + self.assert_iscleanup(mir, block_data, *target, is_cleanup); + }, + TerminatorKind::Resume => if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + }, + TerminatorKind::Return => if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + }, + TerminatorKind::GeneratorDrop { .. } => if is_cleanup { + span_mirbug!(self, block_data, "generator_drop in cleanup block") + }, + TerminatorKind::Yield { resume, drop, .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "yield in cleanup block") + } + self.assert_iscleanup(mir, block_data, resume, is_cleanup); + if let Some(drop) = drop { + self.assert_iscleanup(mir, block_data, drop, is_cleanup); + } + } + TerminatorKind::Unreachable => {} + TerminatorKind::Drop { target, unwind, .. } | + TerminatorKind::DropAndReplace { target, unwind, .. } | + TerminatorKind::Assert { + target, + cleanup: unwind, + .. + } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup); + if let Some(unwind) = unwind { + if is_cleanup { + span_mirbug!(self, block_data, "unwind on cleanup block") + } + self.assert_iscleanup(mir, block_data, unwind, true); + } + } + TerminatorKind::Call { + ref destination, + cleanup, + .. + } => { + if let &Some((_, target)) = destination { + self.assert_iscleanup(mir, block_data, target, is_cleanup); + } + if let Some(cleanup) = cleanup { + if is_cleanup { + span_mirbug!(self, block_data, "cleanup on cleanup block") + } + self.assert_iscleanup(mir, block_data, cleanup, true); + } + } + TerminatorKind::FalseEdges { + real_target, + ref imaginary_targets, + } => { + self.assert_iscleanup(mir, block_data, real_target, is_cleanup); + for target in imaginary_targets { + self.assert_iscleanup(mir, block_data, *target, is_cleanup); + } + } + } + } + + fn assert_iscleanup( + &mut self, + mir: &Mir<'tcx>, + ctxt: &fmt::Debug, + bb: BasicBlock, + iscleanuppad: bool, + ) { + if mir[bb].is_cleanup != iscleanuppad { + span_mirbug!( + self, + ctxt, + "cleanuppad mismatch: {:?} should be {:?}", + bb, + iscleanuppad + ); + } + } + + fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { + match mir.local_kind(local) { + LocalKind::ReturnPointer | LocalKind::Arg => { + // return values of normal functions are required to be + // sized by typeck, but return values of ADT constructors are + // not because we don't include a `Self: Sized` bounds on them. + // + // Unbound parts of arguments were never required to be Sized + // - maybe we should make that a warning. + return; + } + LocalKind::Var | LocalKind::Temp => {} + } + + let span = local_decl.source_info.span; + let ty = local_decl.ty; + + // Erase the regions from `ty` to get a global type. The + // `Sized` bound in no way depends on precise regions, so this + // shouldn't affect `is_sized`. + let gcx = self.tcx().global_tcx(); + let erased_ty = gcx.lift(&self.tcx().erase_regions(&ty)).unwrap(); + if !erased_ty.is_sized(gcx, self.param_env, span) { + // in current MIR construction, all non-control-flow rvalue + // expressions evaluate through `as_temp` or `into` a return + // slot or local, so to find all unsized rvalues it is enough + // to check all temps, return slots and locals. + if let None = self.reported_errors.replace((ty, span)) { + span_err!( + self.tcx().sess, + span, + E0161, + "cannot move a value of type {0}: the size of {0} \ + cannot be statically determined", + ty + ); + } + } + } + + fn aggregate_field_ty( + &mut self, + ak: &AggregateKind<'tcx>, + field_index: usize, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + match *ak { + AggregateKind::Adt(def, variant_index, substs, active_field_index) => { + let variant = &def.variants[variant_index]; + let adj_field_index = active_field_index.unwrap_or(field_index); + if let Some(field) = variant.fields.get(adj_field_index) { + Ok(self.normalize(&field.ty(tcx, substs), location)) + } else { + Err(FieldAccessError::OutOfRange { + field_count: variant.fields.len(), + }) + } + } + AggregateKind::Closure(def_id, substs) => { + match substs.upvar_tys(def_id, tcx).nth(field_index) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.upvar_tys(def_id, tcx).count(), + }), + } + } + AggregateKind::Generator(def_id, substs, _) => { + if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field_index) { + Ok(ty) + } else { + match substs.field_tys(def_id, tcx).nth(field_index) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.field_tys(def_id, tcx).count() + 1, + }), + } + } + } + AggregateKind::Array(ty) => Ok(ty), + AggregateKind::Tuple => { + unreachable!("This should have been covered in check_rvalues"); + } + } + } + + fn check_rvalue(&mut self, mir: &Mir<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + let tcx = self.tcx(); + + match rvalue { + Rvalue::Aggregate(ak, ops) => { + self.check_aggregate_rvalue(mir, rvalue, ak, ops, location) + } + + Rvalue::Repeat(operand, const_usize) => if const_usize.as_u64() > 1 { + let operand_ty = operand.ty(mir, tcx); + + let trait_ref = ty::TraitRef { + def_id: tcx.lang_items().copy_trait().unwrap(), + substs: tcx.mk_substs_trait(operand_ty, &[]), + }; + + self.prove_trait_ref(trait_ref, location); + }, + + Rvalue::NullaryOp(_, ty) => { + let trait_ref = ty::TraitRef { + def_id: tcx.lang_items().sized_trait().unwrap(), + substs: tcx.mk_substs_trait(ty, &[]), + }; + + self.prove_trait_ref(trait_ref, location); + } + + Rvalue::Cast(cast_kind, op, ty) => match cast_kind { + CastKind::ReifyFnPointer => { + let fn_sig = op.ty(mir, tcx).fn_sig(tcx); + + // The type that we see in the fcx is like + // `foo::<'a, 'b>`, where `foo` is the path to a + // function definition. When we extract the + // signature, it comes from the `fn_sig` query, + // and hence may contain unnormalized results. + let fn_sig = self.normalize(&fn_sig, location); + + let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); + + if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); + } + } + + CastKind::ClosureFnPointer => { + let sig = match op.ty(mir, tcx).sty { + ty::TyClosure(def_id, substs) => { + substs.closure_sig_ty(def_id, tcx).fn_sig(tcx) + } + _ => bug!(), + }; + let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig); + + if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); + } + } + + CastKind::UnsafeFnPointer => { + let fn_sig = op.ty(mir, tcx).fn_sig(tcx); + + // The type that we see in the fcx is like + // `foo::<'a, 'b>`, where `foo` is the path to a + // function definition. When we extract the + // signature, it comes from the `fn_sig` query, + // and hence may contain unnormalized results. + let fn_sig = self.normalize(&fn_sig, location); + + let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); + + if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); + } + } + + CastKind::Unsize => { + let trait_ref = ty::TraitRef { + def_id: tcx.lang_items().coerce_unsized_trait().unwrap(), + substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty]), + }; + + self.prove_trait_ref(trait_ref, location); + } + + CastKind::Misc => {} + }, + + // FIXME: These other cases have to be implemented in future PRs + Rvalue::Use(..) | + Rvalue::Ref(..) | + Rvalue::Len(..) | + Rvalue::BinaryOp(..) | + Rvalue::CheckedBinaryOp(..) | + Rvalue::UnaryOp(..) | + Rvalue::Discriminant(..) => {} + } + } + + fn check_aggregate_rvalue( + &mut self, + mir: &Mir<'tcx>, + rvalue: &Rvalue<'tcx>, + aggregate_kind: &AggregateKind<'tcx>, + operands: &[Operand<'tcx>], + location: Location, + ) { + let tcx = self.tcx(); + + self.prove_aggregate_predicates(aggregate_kind, location); + + if *aggregate_kind == AggregateKind::Tuple { + // tuple rvalue field type is always the type of the op. Nothing to check here. + return; + } + + for (i, operand) in operands.iter().enumerate() { + let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { + Ok(field_ty) => field_ty, + Err(FieldAccessError::OutOfRange { field_count }) => { + span_mirbug!( + self, + rvalue, + "accessed field #{} but variant only has {}", + i, + field_count + ); + continue; + } + }; + let operand_ty = operand.ty(mir, tcx); + if let Err(terr) = + self.sub_types(operand_ty, field_ty, location.at_successor_within_block()) + { + span_mirbug!( + self, + rvalue, + "{:?} is not a subtype of {:?}: {:?}", + operand_ty, + field_ty, + terr + ); + } + } + } + + fn prove_aggregate_predicates( + &mut self, + aggregate_kind: &AggregateKind<'tcx>, + location: Location, + ) { + let tcx = self.tcx(); + + debug!( + "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})", + aggregate_kind, + location + ); + + let instantiated_predicates = match aggregate_kind { + AggregateKind::Adt(def, _, substs, _) => { + tcx.predicates_of(def.did).instantiate(tcx, substs) + } + + // For closures, we have some **extra requirements** we + // + // have to check. In particular, in their upvars and + // signatures, closures often reference various regions + // from the surrounding function -- we call those the + // closure's free regions. When we borrow-check (and hence + // region-check) closures, we may find that the closure + // requires certain relationships between those free + // regions. However, because those free regions refer to + // portions of the CFG of their caller, the closure is not + // in a position to verify those relationships. In that + // case, the requirements get "propagated" to us, and so + // we have to solve them here where we instantiate the + // closure. + // + // Despite the opacity of the previous parapgrah, this is + // actually relatively easy to understand in terms of the + // desugaring. A closure gets desugared to a struct, and + // these extra requirements are basically like where + // clauses on the struct. + AggregateKind::Closure(def_id, substs) => { + if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) { + closure_region_requirements.apply_requirements( + self.infcx, + location, + *def_id, + *substs, + ); + } + + tcx.predicates_of(*def_id).instantiate(tcx, substs.substs) + } + + AggregateKind::Generator(def_id, substs, _) => { + tcx.predicates_of(*def_id).instantiate(tcx, substs.substs) + } + + AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), + }; + + let predicates = self.normalize(&instantiated_predicates.predicates, location); + debug!("prove_aggregate_predicates: predicates={:?}", predicates); + self.prove_predicates(&predicates, location); + } + + fn prove_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>, location: Location) { + self.prove_predicates( + &[ + ty::Predicate::Trait(trait_ref.to_poly_trait_ref().to_poly_trait_predicate()), + ], + location, + ); + } + + fn prove_predicates(&mut self, predicates: &[ty::Predicate<'tcx>], location: Location) { + debug!( + "prove_predicates(predicates={:?}, location={:?})", + predicates, + location + ); + self.fully_perform_op(location.at_self(), |this| { + let cause = this.misc(this.last_span); + let obligations = predicates + .iter() + .map(|&p| { + traits::Obligation::new(cause.clone(), this.param_env, p) + }) + .collect(); + Ok(InferOk { + value: (), + obligations, + }) + }).unwrap() + } + + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { + self.last_span = mir.span; + debug!("run_on_mir: {:?}", mir.span); + + for (local, local_decl) in mir.local_decls.iter_enumerated() { + self.check_local(mir, local, local_decl); + } + + for (block, block_data) in mir.basic_blocks().iter_enumerated() { + let mut location = Location { + block, + statement_index: 0, + }; + for stmt in &block_data.statements { + if stmt.source_info.span != DUMMY_SP { + self.last_span = stmt.source_info.span; + } + self.check_stmt(mir, stmt, location); + location.statement_index += 1; + } + + self.check_terminator(mir, block_data.terminator(), location); + self.check_iscleanup(mir, block_data); + } + } + + fn normalize(&mut self, value: &T, location: Location) -> T + where + T: fmt::Debug + TypeFoldable<'tcx>, + { + self.fully_perform_op(location.at_self(), |this| { + let mut selcx = traits::SelectionContext::new(this.infcx); + let cause = this.misc(this.last_span); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, this.param_env, cause, value); + Ok(InferOk { value, obligations }) + }).unwrap() + } +} + +pub struct TypeckMir; + +impl MirPass for TypeckMir { + fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { + let def_id = src.def_id; + let id = tcx.hir.as_local_node_id(def_id).unwrap(); + debug!("run_pass: {:?}", def_id); + + if tcx.sess.err_count() > 0 { + // compiling a broken program can obviously result in a + // broken MIR, so try not to report duplicate errors. + return; + } + let param_env = tcx.param_env(def_id); + tcx.infer_ctxt().enter(|infcx| { + let _region_constraint_sets = type_check(&infcx, id, param_env, mir); + + // For verification purposes, we just ignore the resulting + // region constraint sets. Not our problem. =) + }); + } +} + +trait AtLocation { + /// Creates a `Locations` where `self` is both the from-location + /// and the at-location. This means that any required region + /// relationships must hold upon entering the statement/terminator + /// indicated by `self`. This is typically used when processing + /// "inputs" to the given location. + fn at_self(self) -> Locations; + + /// Creates a `Locations` where `self` is the from-location and + /// its successor within the block is the at-location. This means + /// that any required region relationships must hold only upon + /// **exiting** the statement/terminator indicated by `self`. This + /// is for example used when you have a `place = rv` statement: it + /// indicates that the `typeof(rv) <: typeof(place)` as of the + /// **next** statement. + fn at_successor_within_block(self) -> Locations; +} + +impl AtLocation for Location { + fn at_self(self) -> Locations { + Locations { + from_location: self, + at_location: self, + } + } + + fn at_successor_within_block(self) -> Locations { + Locations { + from_location: self, + at_location: self.successor_within_block(), + } + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index fb9daf07c71..563405fccc9 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use borrow_check::nll::type_check; use build; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Mir, Promoted}; @@ -30,7 +31,6 @@ pub mod simplify_branches; pub mod simplify; pub mod erase_regions; pub mod no_landing_pads; -pub mod type_check; pub mod rustc_peek; pub mod elaborate_drops; pub mod add_call_guards; diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs deleted file mode 100644 index fae911780a1..00000000000 --- a/src/librustc_mir/transform/type_check.rs +++ /dev/null @@ -1,1517 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! This pass type-checks the MIR to ensure it is not broken. -#![allow(unreachable_code)] - -use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; -use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; -use rustc::infer::region_constraints::RegionConstraintData; -use rustc::traits::{self, FulfillmentContext}; -use rustc::ty::error::TypeError; -use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; -use rustc::middle::const_val::ConstVal; -use rustc::mir::*; -use rustc::mir::tcx::PlaceTy; -use rustc::mir::visit::{PlaceContext, Visitor}; -use std::fmt; -use syntax::ast; -use syntax_pos::{Span, DUMMY_SP}; -use transform::{MirPass, MirSource}; - -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_vec::Idx; - -/// Type checks the given `mir` in the context of the inference -/// context `infcx`. Returns any region constraints that have yet to -/// be proven. -/// -/// This phase of type-check ought to be infallible -- this is because -/// the original, HIR-based type-check succeeded. So if any errors -/// occur here, we will get a `bug!` reported. -pub fn type_check<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'gcx>, - mir: &Mir<'tcx>, -) -> MirTypeckRegionConstraints<'tcx> { - let mut checker = TypeChecker::new(infcx, body_id, param_env); - let errors_reported = { - let mut verifier = TypeVerifier::new(&mut checker, mir); - verifier.visit_mir(mir); - verifier.errors_reported - }; - - if !errors_reported { - // if verifier failed, don't do further checks to avoid ICEs - checker.typeck_mir(mir); - } - - checker.constraints -} - -fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { - // We sometimes see MIR failures (notably predicate failures) due to - // the fact that we check rvalue sized predicates here. So use `delay_span_bug` - // to avoid reporting bugs in those cases. - tcx.sess.diagnostic().delay_span_bug(span, msg); -} - -macro_rules! span_mirbug { - ($context:expr, $elem:expr, $($message:tt)*) => ({ - mirbug($context.tcx(), $context.last_span, - &format!("broken MIR in {:?} ({:?}): {}", - $context.body_id, - $elem, - format_args!($($message)*))) - }) -} - -macro_rules! span_mirbug_and_err { - ($context:expr, $elem:expr, $($message:tt)*) => ({ - { - span_mirbug!($context, $elem, $($message)*); - $context.error() - } - }) -} - -enum FieldAccessError { - OutOfRange { field_count: usize }, -} - -/// Verifies that MIR types are sane to not crash further checks. -/// -/// The sanitize_XYZ methods here take an MIR object and compute its -/// type, calling `span_mirbug` and returning an error type if there -/// is a problem. -struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> { - cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, - mir: &'a Mir<'tcx>, - last_span: Span, - body_id: ast::NodeId, - errors_reported: bool, -} - -impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { - fn visit_span(&mut self, span: &Span) { - if *span != DUMMY_SP { - self.last_span = *span; - } - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.sanitize_place(place, location, context); - } - - fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { - self.super_constant(constant, location); - self.sanitize_constant(constant, location); - self.sanitize_type(constant, constant.ty); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - self.super_rvalue(rvalue, location); - let rval_ty = rvalue.ty(self.mir, self.tcx()); - self.sanitize_type(rvalue, rval_ty); - } - - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - self.super_local_decl(local, local_decl); - self.sanitize_type(local_decl, local_decl.ty); - } - - fn visit_mir(&mut self, mir: &Mir<'tcx>) { - self.sanitize_type(&"return type", mir.return_ty()); - for local_decl in &mir.local_decls { - self.sanitize_type(local_decl, local_decl.ty); - } - if self.errors_reported { - return; - } - self.super_mir(mir); - } -} - -impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { - fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { - TypeVerifier { - mir, - body_id: cx.body_id, - cx, - last_span: mir.span, - errors_reported: false, - } - } - - fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { - self.cx.infcx.tcx - } - - fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_regions() || ty.references_error() { - span_mirbug_and_err!(self, parent, "bad type {:?}", ty) - } else { - ty - } - } - - /// Checks that the constant's `ty` field matches up with what - /// would be expected from its literal. - fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) { - debug!( - "sanitize_constant(constant={:?}, location={:?})", - constant, - location - ); - - let expected_ty = match constant.literal { - Literal::Value { value } => { - // FIXME(#46702) -- We need some way to get the predicates - // associated with the "pre-evaluated" form of the - // constant. For example, consider that the constant - // may have associated constant projections (`>::SOME_CONST`) that impose - // constraints on `'a` and `'b`. These constraints - // would be lost if we just look at the normalized - // value. - if let ConstVal::Function(def_id, ..) = value.val { - let tcx = self.tcx(); - let type_checker = &mut self.cx; - - // FIXME -- For now, use the substitutions from - // `value.ty` rather than `value.val`. The - // renumberer will rewrite them to independent - // sets of regions; in principle, we ought to - // derive the type of the `value.val` from "first - // principles" and equate with value.ty, but as we - // are transitioning to the miri-based system, we - // don't have a handy function for that, so for - // now we just ignore `value.val` regions. - let substs = match value.ty.sty { - ty::TyFnDef(ty_def_id, substs) => { - assert_eq!(def_id, ty_def_id); - substs - } - _ => span_bug!( - self.last_span, - "unexpected type for constant function: {:?}", - value.ty - ), - }; - - let instantiated_predicates = - tcx.predicates_of(def_id).instantiate(tcx, substs); - let predicates = - type_checker.normalize(&instantiated_predicates.predicates, location); - type_checker.prove_predicates(&predicates, location); - } - - value.ty - } - - Literal::Promoted { .. } => { - // FIXME -- promoted MIR return types reference - // various "free regions" (e.g., scopes and things) - // that they ought not to do. We have to figure out - // how best to handle that -- probably we want treat - // promoted MIR much like closures, renumbering all - // their free regions and propagating constraints - // upwards. We have the same acyclic guarantees, so - // that should be possible. But for now, ignore them. - // - // let promoted_mir = &self.mir.promoted[index]; - // promoted_mir.return_ty() - return; - } - }; - - debug!("sanitize_constant: expected_ty={:?}", expected_ty); - - if let Err(terr) = self.cx - .eq_types(expected_ty, constant.ty, location.at_self()) - { - span_mirbug!( - self, - constant, - "constant {:?} should have type {:?} but has {:?} ({:?})", - constant, - expected_ty, - constant.ty, - terr, - ); - } - } - - /// Checks that the types internal to the `place` match up with - /// what would be expected. - fn sanitize_place( - &mut self, - place: &Place<'tcx>, - location: Location, - context: PlaceContext, - ) -> PlaceTy<'tcx> { - debug!("sanitize_place: {:?}", place); - let place_ty = match *place { - Place::Local(index) => PlaceTy::Ty { - ty: self.mir.local_decls[index].ty, - }, - Place::Static(box Static { def_id, ty: sty }) => { - let sty = self.sanitize_type(place, sty); - let ty = self.tcx().type_of(def_id); - let ty = self.cx.normalize(&ty, location); - if let Err(terr) = self.cx.eq_types(ty, sty, location.at_self()) { - span_mirbug!( - self, - place, - "bad static type ({:?}: {:?}): {:?}", - ty, - sty, - terr - ); - } - PlaceTy::Ty { ty: sty } - } - Place::Projection(ref proj) => { - let base_context = if context.is_mutating_use() { - PlaceContext::Projection(Mutability::Mut) - } else { - PlaceContext::Projection(Mutability::Not) - }; - let base_ty = self.sanitize_place(&proj.base, location, base_context); - if let PlaceTy::Ty { ty } = base_ty { - if ty.references_error() { - assert!(self.errors_reported); - return PlaceTy::Ty { - ty: self.tcx().types.err, - }; - } - } - self.sanitize_projection(base_ty, &proj.elem, place, location) - } - }; - if let PlaceContext::Copy = context { - let ty = place_ty.to_ty(self.tcx()); - if self.cx - .infcx - .type_moves_by_default(self.cx.param_env, ty, DUMMY_SP) - { - span_mirbug!(self, place, "attempted copy of non-Copy type ({:?})", ty); - } - } - place_ty - } - - fn sanitize_projection( - &mut self, - base: PlaceTy<'tcx>, - pi: &PlaceElem<'tcx>, - place: &Place<'tcx>, - location: Location, - ) -> PlaceTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); - let tcx = self.tcx(); - let base_ty = base.to_ty(tcx); - match *pi { - ProjectionElem::Deref => { - let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); - PlaceTy::Ty { - ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { - span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty) - }), - } - } - ProjectionElem::Index(i) => { - let index_ty = Place::Local(i).ty(self.mir, tcx).to_ty(tcx); - if index_ty != tcx.types.usize { - PlaceTy::Ty { - ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i), - } - } else { - PlaceTy::Ty { - ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) - }), - } - } - } - ProjectionElem::ConstantIndex { .. } => { - // consider verifying in-bounds - PlaceTy::Ty { - ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) - }), - } - } - ProjectionElem::Subslice { from, to } => PlaceTy::Ty { - ty: match base_ty.sty { - ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); - let min_size = (from as u64) + (to as u64); - if let Some(rest_size) = size.checked_sub(min_size) { - tcx.mk_array(inner, rest_size) - } else { - span_mirbug_and_err!( - self, - place, - "taking too-small slice of {:?}", - base_ty - ) - } - } - ty::TySlice(..) => base_ty, - _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), - }, - }, - ProjectionElem::Downcast(adt_def1, index) => match base_ty.sty { - ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { - if index >= adt_def.variants.len() { - PlaceTy::Ty { - ty: span_mirbug_and_err!( - self, - place, - "cast to variant #{:?} but enum only has {:?}", - index, - adt_def.variants.len() - ), - } - } else { - PlaceTy::Downcast { - adt_def, - substs, - variant_index: index, - } - } - } - _ => PlaceTy::Ty { - ty: span_mirbug_and_err!( - self, - place, - "can't downcast {:?} as {:?}", - base_ty, - adt_def1 - ), - }, - }, - ProjectionElem::Field(field, fty) => { - let fty = self.sanitize_type(place, fty); - match self.field_ty(place, base, field, location) { - Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) { - span_mirbug!( - self, - place, - "bad field access ({:?}: {:?}): {:?}", - ty, - fty, - terr - ); - }, - Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( - self, - place, - "accessed field #{} but variant only has {}", - field.index(), - field_count - ), - } - PlaceTy::Ty { ty: fty } - } - } - } - - fn error(&mut self) -> Ty<'tcx> { - self.errors_reported = true; - self.tcx().types.err - } - - fn field_ty( - &mut self, - parent: &fmt::Debug, - base_ty: PlaceTy<'tcx>, - field: Field, - location: Location, - ) -> Result, FieldAccessError> { - let tcx = self.tcx(); - - let (variant, substs) = match base_ty { - PlaceTy::Downcast { - adt_def, - substs, - variant_index, - } => (&adt_def.variants[variant_index], substs), - PlaceTy::Ty { ty } => match ty.sty { - ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => (&adt_def.variants[0], substs), - ty::TyClosure(def_id, substs) => { - return match substs.upvar_tys(def_id, tcx).nth(field.index()) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.upvar_tys(def_id, tcx).count(), - }), - } - } - ty::TyGenerator(def_id, substs, _) => { - // Try upvars first. `field_tys` requires final optimized MIR. - if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field.index()) { - return Ok(ty); - } - - return match substs.field_tys(def_id, tcx).nth(field.index()) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.field_tys(def_id, tcx).count() + 1, - }), - }; - } - ty::TyTuple(tys, _) => { - return match tys.get(field.index()) { - Some(&ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: tys.len(), - }), - } - } - _ => { - return Ok(span_mirbug_and_err!( - self, - parent, - "can't project out of {:?}", - base_ty - )) - } - }, - }; - - if let Some(field) = variant.fields.get(field.index()) { - Ok(self.cx.normalize(&field.ty(tcx, substs), location)) - } else { - Err(FieldAccessError::OutOfRange { - field_count: variant.fields.len(), - }) - } - } -} - -/// The MIR type checker. Visits the MIR and enforces all the -/// constraints needed for it to be valid and well-typed. Along the -/// way, it accrues region constraints -- these can later be used by -/// NLL region checking. -pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'gcx>, - last_span: Span, - body_id: ast::NodeId, - reported_errors: FxHashSet<(Ty<'tcx>, Span)>, - constraints: MirTypeckRegionConstraints<'tcx>, -} - -/// A collection of region constraints that must be satisfied for the -/// program to be considered well-typed. -#[derive(Default)] -pub struct MirTypeckRegionConstraints<'tcx> { - /// In general, the type-checker is not responsible for enforcing - /// liveness constraints; this job falls to the region inferencer, - /// which performs a liveness analysis. However, in some limited - /// cases, the MIR type-checker creates temporary regions that do - /// 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. - pub liveness_set: Vec<(ty::Region<'tcx>, Location)>, - - /// During the course of type-checking, we will accumulate region - /// constraints due to performing subtyping operations or solving - /// traits. These are accumulated into this vector for later use. - pub outlives_sets: Vec>, -} - -/// Outlives relationships between regions and types created at a -/// particular point within the control-flow graph. -pub struct OutlivesSet<'tcx> { - /// The locations associated with these constraints. - pub locations: Locations, - - /// Constraints generated. In terms of the NLL RFC, when you have - /// a constraint `R1: R2 @ P`, the data in there specifies things - /// like `R1: R2`. - pub data: RegionConstraintData<'tcx>, -} - -#[derive(Copy, Clone, Debug)] -pub struct Locations { - /// The location in the MIR that generated these constraints. - /// This is intended for error reporting and diagnosis; the - /// constraints may *take effect* at a distinct spot. - pub from_location: Location, - - /// The constraints must be met at this location. In terms of the - /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field - /// is the `P` value. - pub at_location: Location, -} - -impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { - fn new( - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'gcx>, - ) -> Self { - TypeChecker { - infcx, - last_span: DUMMY_SP, - body_id, - param_env, - reported_errors: FxHashSet(), - constraints: MirTypeckRegionConstraints::default(), - } - } - - fn misc(&self, span: Span) -> traits::ObligationCause<'tcx> { - traits::ObligationCause::misc(span, self.body_id) - } - - fn fully_perform_op( - &mut self, - locations: Locations, - op: OP, - ) -> Result> - where - OP: FnOnce(&mut Self) -> InferResult<'tcx, R>, - { - let mut fulfill_cx = FulfillmentContext::new(); - let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; - fulfill_cx.register_predicate_obligations(self.infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { - span_mirbug!(self, "", "errors selecting obligation: {:?}", e); - } - - self.infcx - .process_registered_region_obligations(&[], None, self.param_env, self.body_id); - - let data = self.infcx.take_and_reset_region_constraints(); - if !data.is_empty() { - self.constraints - .outlives_sets - .push(OutlivesSet { locations, data }); - } - - Ok(value) - } - - fn sub_types( - &mut self, - sub: Ty<'tcx>, - sup: Ty<'tcx>, - locations: Locations, - ) -> UnitResult<'tcx> { - self.fully_perform_op(locations, |this| { - this.infcx - .at(&this.misc(this.last_span), this.param_env) - .sup(sup, sub) - }) - } - - fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { - self.fully_perform_op(locations, |this| { - this.infcx - .at(&this.misc(this.last_span), this.param_env) - .eq(b, a) - }) - } - - fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { - self.infcx.tcx - } - - fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>, location: Location) { - debug!("check_stmt: {:?}", stmt); - let tcx = self.tcx(); - match stmt.kind { - StatementKind::Assign(ref place, ref rv) => { - let place_ty = place.ty(mir, tcx).to_ty(tcx); - let rv_ty = rv.ty(mir, tcx); - if let Err(terr) = - self.sub_types(rv_ty, place_ty, location.at_successor_within_block()) - { - span_mirbug!( - self, - stmt, - "bad assignment ({:?} = {:?}): {:?}", - place_ty, - rv_ty, - terr - ); - } - self.check_rvalue(mir, rv, location); - } - StatementKind::SetDiscriminant { - ref place, - variant_index, - } => { - let place_type = place.ty(mir, tcx).to_ty(tcx); - let adt = match place_type.sty { - TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt, - _ => { - span_bug!( - stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): lhs is not an enum", - place, - variant_index - ); - } - }; - if variant_index >= adt.variants.len() { - span_bug!( - stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): value of of range", - place, - variant_index - ); - }; - } - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) | - StatementKind::InlineAsm { .. } | - StatementKind::EndRegion(_) | - StatementKind::Validate(..) | - StatementKind::Nop => {} - } - } - - fn check_terminator( - &mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - term_location: Location, - ) { - debug!("check_terminator: {:?}", term); - let tcx = self.tcx(); - match term.kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable | - TerminatorKind::Drop { .. } | - TerminatorKind::FalseEdges { .. } => { - // no checks needed for these - } - - TerminatorKind::DropAndReplace { - ref location, - ref value, - target, - unwind, - } => { - let place_ty = location.ty(mir, tcx).to_ty(tcx); - let rv_ty = value.ty(mir, tcx); - - let locations = Locations { - from_location: term_location, - at_location: target.start_location(), - }; - if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) { - span_mirbug!( - self, - term, - "bad DropAndReplace ({:?} = {:?}): {:?}", - place_ty, - rv_ty, - terr - ); - } - - // Subtle: this assignment occurs at the start of - // *both* blocks, so we need to ensure that it holds - // at both locations. - if let Some(unwind) = unwind { - let locations = Locations { - from_location: term_location, - at_location: unwind.start_location(), - }; - if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) { - span_mirbug!( - self, - term, - "bad DropAndReplace ({:?} = {:?}): {:?}", - place_ty, - rv_ty, - terr - ); - } - } - } - TerminatorKind::SwitchInt { - ref discr, - switch_ty, - .. - } => { - let discr_ty = discr.ty(mir, tcx); - if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.at_self()) { - span_mirbug!( - self, - term, - "bad SwitchInt ({:?} on {:?}): {:?}", - switch_ty, - discr_ty, - terr - ); - } - if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { - span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); - } - // FIXME: check the values - } - TerminatorKind::Call { - ref func, - ref args, - ref destination, - .. - } => { - let func_ty = func.ty(mir, tcx); - debug!("check_terminator: call, func_ty={:?}", func_ty); - let sig = match func_ty.sty { - ty::TyFnDef(..) | ty::TyFnPtr(_) => func_ty.fn_sig(tcx), - _ => { - span_mirbug!(self, term, "call to non-function {:?}", func_ty); - return; - } - }; - let (sig, map) = self.infcx.replace_late_bound_regions_with_fresh_var( - term.source_info.span, - LateBoundRegionConversionTime::FnCall, - &sig, - ); - let sig = self.normalize(&sig, term_location); - self.check_call_dest(mir, term, &sig, destination, term_location); - - // The ordinary liveness rules will ensure that all - // regions in the type of the callee are live here. We - // then further constrain the late-bound regions that - // were instantiated at the call site to be live as - // well. The resulting is that all the input (and - // output) types in the signature must be live, since - // all the inputs that fed into it were live. - for &late_bound_region in map.values() { - self.constraints - .liveness_set - .push((late_bound_region, term_location)); - } - - if self.is_box_free(func) { - self.check_box_free_inputs(mir, term, &sig, args, term_location); - } else { - self.check_call_inputs(mir, term, &sig, args, term_location); - } - } - TerminatorKind::Assert { - ref cond, ref msg, .. - } => { - let cond_ty = cond.ty(mir, tcx); - if cond_ty != tcx.types.bool { - span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); - } - - if let AssertMessage::BoundsCheck { ref len, ref index } = *msg { - if len.ty(mir, tcx) != tcx.types.usize { - span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) - } - if index.ty(mir, tcx) != tcx.types.usize { - span_mirbug!(self, index, "bounds-check index non-usize {:?}", index) - } - } - } - TerminatorKind::Yield { ref value, .. } => { - let value_ty = value.ty(mir, tcx); - match mir.yield_ty { - None => span_mirbug!(self, term, "yield in non-generator"), - Some(ty) => { - if let Err(terr) = self.sub_types(value_ty, ty, term_location.at_self()) { - span_mirbug!( - self, - term, - "type of yield value is {:?}, but the yield type is {:?}: {:?}", - value_ty, - ty, - terr - ); - } - } - } - } - } - } - - fn check_call_dest( - &mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: &Option<(Place<'tcx>, BasicBlock)>, - term_location: Location, - ) { - let tcx = self.tcx(); - match *destination { - Some((ref dest, target_block)) => { - let dest_ty = dest.ty(mir, tcx).to_ty(tcx); - let locations = Locations { - from_location: term_location, - at_location: target_block.start_location(), - }; - if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) { - span_mirbug!( - self, - term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, - sig.output(), - terr - ); - } - } - None => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - if !sig.output().is_never() { - span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); - } - } - } - } - - fn check_call_inputs( - &mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>], - term_location: Location, - ) { - debug!("check_call_inputs({:?}, {:?})", sig, args); - if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) { - span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); - } - for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { - let op_arg_ty = op_arg.ty(mir, self.tcx()); - if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.at_self()) { - span_mirbug!( - self, - term, - "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, - fn_arg, - op_arg_ty, - terr - ); - } - } - } - - fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { - match operand { - &Operand::Constant(box Constant { - literal: - Literal::Value { - value: - &ty::Const { - val: ConstVal::Function(def_id, _), - .. - }, - .. - }, - .. - }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), - _ => false, - } - } - - fn check_box_free_inputs( - &mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>], - term_location: Location, - ) { - debug!("check_box_free_inputs"); - - // box_free takes a Box as a pointer. Allow for that. - - if sig.inputs().len() != 1 { - span_mirbug!(self, term, "box_free should take 1 argument"); - return; - } - - let pointee_ty = match sig.inputs()[0].sty { - ty::TyRawPtr(mt) => mt.ty, - _ => { - span_mirbug!(self, term, "box_free should take a raw ptr"); - return; - } - }; - - if args.len() != 1 { - span_mirbug!(self, term, "box_free called with wrong # of args"); - return; - } - - let ty = args[0].ty(mir, self.tcx()); - let arg_ty = match ty.sty { - ty::TyRawPtr(mt) => mt.ty, - ty::TyAdt(def, _) if def.is_box() => ty.boxed_ty(), - _ => { - span_mirbug!(self, term, "box_free called with bad arg ty"); - return; - } - }; - - if let Err(terr) = self.sub_types(arg_ty, pointee_ty, term_location.at_self()) { - span_mirbug!( - self, - term, - "bad box_free arg ({:?} <- {:?}): {:?}", - pointee_ty, - arg_ty, - terr - ); - } - } - - fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) { - let is_cleanup = block_data.is_cleanup; - self.last_span = block_data.terminator().source_info.span; - match block_data.terminator().kind { - TerminatorKind::Goto { target } => { - self.assert_iscleanup(mir, block_data, target, is_cleanup) - } - TerminatorKind::SwitchInt { ref targets, .. } => for target in targets { - self.assert_iscleanup(mir, block_data, *target, is_cleanup); - }, - TerminatorKind::Resume => if !is_cleanup { - span_mirbug!(self, block_data, "resume on non-cleanup block!") - }, - TerminatorKind::Return => if is_cleanup { - span_mirbug!(self, block_data, "return on cleanup block") - }, - TerminatorKind::GeneratorDrop { .. } => if is_cleanup { - span_mirbug!(self, block_data, "generator_drop in cleanup block") - }, - TerminatorKind::Yield { resume, drop, .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "yield in cleanup block") - } - self.assert_iscleanup(mir, block_data, resume, is_cleanup); - if let Some(drop) = drop { - self.assert_iscleanup(mir, block_data, drop, is_cleanup); - } - } - TerminatorKind::Unreachable => {} - TerminatorKind::Drop { target, unwind, .. } | - TerminatorKind::DropAndReplace { target, unwind, .. } | - TerminatorKind::Assert { - target, - cleanup: unwind, - .. - } => { - self.assert_iscleanup(mir, block_data, target, is_cleanup); - if let Some(unwind) = unwind { - if is_cleanup { - span_mirbug!(self, block_data, "unwind on cleanup block") - } - self.assert_iscleanup(mir, block_data, unwind, true); - } - } - TerminatorKind::Call { - ref destination, - cleanup, - .. - } => { - if let &Some((_, target)) = destination { - self.assert_iscleanup(mir, block_data, target, is_cleanup); - } - if let Some(cleanup) = cleanup { - if is_cleanup { - span_mirbug!(self, block_data, "cleanup on cleanup block") - } - self.assert_iscleanup(mir, block_data, cleanup, true); - } - } - TerminatorKind::FalseEdges { - real_target, - ref imaginary_targets, - } => { - self.assert_iscleanup(mir, block_data, real_target, is_cleanup); - for target in imaginary_targets { - self.assert_iscleanup(mir, block_data, *target, is_cleanup); - } - } - } - } - - fn assert_iscleanup( - &mut self, - mir: &Mir<'tcx>, - ctxt: &fmt::Debug, - bb: BasicBlock, - iscleanuppad: bool, - ) { - if mir[bb].is_cleanup != iscleanuppad { - span_mirbug!( - self, - ctxt, - "cleanuppad mismatch: {:?} should be {:?}", - bb, - iscleanuppad - ); - } - } - - fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { - match mir.local_kind(local) { - LocalKind::ReturnPointer | LocalKind::Arg => { - // return values of normal functions are required to be - // sized by typeck, but return values of ADT constructors are - // not because we don't include a `Self: Sized` bounds on them. - // - // Unbound parts of arguments were never required to be Sized - // - maybe we should make that a warning. - return; - } - LocalKind::Var | LocalKind::Temp => {} - } - - let span = local_decl.source_info.span; - let ty = local_decl.ty; - - // Erase the regions from `ty` to get a global type. The - // `Sized` bound in no way depends on precise regions, so this - // shouldn't affect `is_sized`. - let gcx = self.tcx().global_tcx(); - let erased_ty = gcx.lift(&self.tcx().erase_regions(&ty)).unwrap(); - if !erased_ty.is_sized(gcx, self.param_env, span) { - // in current MIR construction, all non-control-flow rvalue - // expressions evaluate through `as_temp` or `into` a return - // slot or local, so to find all unsized rvalues it is enough - // to check all temps, return slots and locals. - if let None = self.reported_errors.replace((ty, span)) { - span_err!( - self.tcx().sess, - span, - E0161, - "cannot move a value of type {0}: the size of {0} \ - cannot be statically determined", - ty - ); - } - } - } - - fn aggregate_field_ty( - &mut self, - ak: &AggregateKind<'tcx>, - field_index: usize, - location: Location, - ) -> Result, FieldAccessError> { - let tcx = self.tcx(); - - match *ak { - AggregateKind::Adt(def, variant_index, substs, active_field_index) => { - let variant = &def.variants[variant_index]; - let adj_field_index = active_field_index.unwrap_or(field_index); - if let Some(field) = variant.fields.get(adj_field_index) { - Ok(self.normalize(&field.ty(tcx, substs), location)) - } else { - Err(FieldAccessError::OutOfRange { - field_count: variant.fields.len(), - }) - } - } - AggregateKind::Closure(def_id, substs) => { - match substs.upvar_tys(def_id, tcx).nth(field_index) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.upvar_tys(def_id, tcx).count(), - }), - } - } - AggregateKind::Generator(def_id, substs, _) => { - if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field_index) { - Ok(ty) - } else { - match substs.field_tys(def_id, tcx).nth(field_index) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.field_tys(def_id, tcx).count() + 1, - }), - } - } - } - AggregateKind::Array(ty) => Ok(ty), - AggregateKind::Tuple => { - unreachable!("This should have been covered in check_rvalues"); - } - } - } - - fn check_rvalue(&mut self, mir: &Mir<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { - let tcx = self.tcx(); - - match rvalue { - Rvalue::Aggregate(ak, ops) => { - self.check_aggregate_rvalue(mir, rvalue, ak, ops, location) - } - - Rvalue::Repeat(operand, const_usize) => if const_usize.as_u64() > 1 { - let operand_ty = operand.ty(mir, tcx); - - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().copy_trait().unwrap(), - substs: tcx.mk_substs_trait(operand_ty, &[]), - }; - - self.prove_trait_ref(trait_ref, location); - }, - - Rvalue::NullaryOp(_, ty) => { - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().sized_trait().unwrap(), - substs: tcx.mk_substs_trait(ty, &[]), - }; - - self.prove_trait_ref(trait_ref, location); - } - - Rvalue::Cast(cast_kind, op, ty) => match cast_kind { - CastKind::ReifyFnPointer => { - let fn_sig = op.ty(mir, tcx).fn_sig(tcx); - - // The type that we see in the fcx is like - // `foo::<'a, 'b>`, where `foo` is the path to a - // function definition. When we extract the - // signature, it comes from the `fn_sig` query, - // and hence may contain unnormalized results. - let fn_sig = self.normalize(&fn_sig, location); - - let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); - - if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); - } - } - - CastKind::ClosureFnPointer => { - let sig = match op.ty(mir, tcx).sty { - ty::TyClosure(def_id, substs) => { - substs.closure_sig_ty(def_id, tcx).fn_sig(tcx) - } - _ => bug!(), - }; - let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig); - - if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); - } - } - - CastKind::UnsafeFnPointer => { - let fn_sig = op.ty(mir, tcx).fn_sig(tcx); - - // The type that we see in the fcx is like - // `foo::<'a, 'b>`, where `foo` is the path to a - // function definition. When we extract the - // signature, it comes from the `fn_sig` query, - // and hence may contain unnormalized results. - let fn_sig = self.normalize(&fn_sig, location); - - let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); - - if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); - } - } - - CastKind::Unsize => { - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().coerce_unsized_trait().unwrap(), - substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty]), - }; - - self.prove_trait_ref(trait_ref, location); - } - - CastKind::Misc => {} - }, - - // FIXME: These other cases have to be implemented in future PRs - Rvalue::Use(..) | - Rvalue::Ref(..) | - Rvalue::Len(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) => {} - } - } - - fn check_aggregate_rvalue( - &mut self, - mir: &Mir<'tcx>, - rvalue: &Rvalue<'tcx>, - aggregate_kind: &AggregateKind<'tcx>, - operands: &[Operand<'tcx>], - location: Location, - ) { - let tcx = self.tcx(); - - self.prove_aggregate_predicates(aggregate_kind, location); - - if *aggregate_kind == AggregateKind::Tuple { - // tuple rvalue field type is always the type of the op. Nothing to check here. - return; - } - - for (i, operand) in operands.iter().enumerate() { - let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { - Ok(field_ty) => field_ty, - Err(FieldAccessError::OutOfRange { field_count }) => { - span_mirbug!( - self, - rvalue, - "accessed field #{} but variant only has {}", - i, - field_count - ); - continue; - } - }; - let operand_ty = operand.ty(mir, tcx); - if let Err(terr) = - self.sub_types(operand_ty, field_ty, location.at_successor_within_block()) - { - span_mirbug!( - self, - rvalue, - "{:?} is not a subtype of {:?}: {:?}", - operand_ty, - field_ty, - terr - ); - } - } - } - - fn prove_aggregate_predicates( - &mut self, - aggregate_kind: &AggregateKind<'tcx>, - location: Location, - ) { - let tcx = self.tcx(); - - debug!( - "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})", - aggregate_kind, - location - ); - - let instantiated_predicates = match aggregate_kind { - AggregateKind::Adt(def, _, substs, _) => { - tcx.predicates_of(def.did).instantiate(tcx, substs) - } - - // For closures, we have some **extra requirements** we - // - // have to check. In particular, in their upvars and - // signatures, closures often reference various regions - // from the surrounding function -- we call those the - // closure's free regions. When we borrow-check (and hence - // region-check) closures, we may find that the closure - // requires certain relationships between those free - // regions. However, because those free regions refer to - // portions of the CFG of their caller, the closure is not - // in a position to verify those relationships. In that - // case, the requirements get "propagated" to us, and so - // we have to solve them here where we instantiate the - // closure. - // - // Despite the opacity of the previous parapgrah, this is - // actually relatively easy to understand in terms of the - // desugaring. A closure gets desugared to a struct, and - // these extra requirements are basically like where - // clauses on the struct. - AggregateKind::Closure(def_id, substs) => { - if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) { - closure_region_requirements.apply_requirements( - self.infcx, - location, - *def_id, - *substs, - ); - } - - tcx.predicates_of(*def_id).instantiate(tcx, substs.substs) - } - - AggregateKind::Generator(def_id, substs, _) => { - tcx.predicates_of(*def_id).instantiate(tcx, substs.substs) - } - - AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), - }; - - let predicates = self.normalize(&instantiated_predicates.predicates, location); - debug!("prove_aggregate_predicates: predicates={:?}", predicates); - self.prove_predicates(&predicates, location); - } - - fn prove_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>, location: Location) { - self.prove_predicates( - &[ - ty::Predicate::Trait(trait_ref.to_poly_trait_ref().to_poly_trait_predicate()), - ], - location, - ); - } - - fn prove_predicates(&mut self, predicates: &[ty::Predicate<'tcx>], location: Location) { - debug!( - "prove_predicates(predicates={:?}, location={:?})", - predicates, - location - ); - self.fully_perform_op(location.at_self(), |this| { - let cause = this.misc(this.last_span); - let obligations = predicates - .iter() - .map(|&p| { - traits::Obligation::new(cause.clone(), this.param_env, p) - }) - .collect(); - Ok(InferOk { - value: (), - obligations, - }) - }).unwrap() - } - - fn typeck_mir(&mut self, mir: &Mir<'tcx>) { - self.last_span = mir.span; - debug!("run_on_mir: {:?}", mir.span); - - for (local, local_decl) in mir.local_decls.iter_enumerated() { - self.check_local(mir, local, local_decl); - } - - for (block, block_data) in mir.basic_blocks().iter_enumerated() { - let mut location = Location { - block, - statement_index: 0, - }; - for stmt in &block_data.statements { - if stmt.source_info.span != DUMMY_SP { - self.last_span = stmt.source_info.span; - } - self.check_stmt(mir, stmt, location); - location.statement_index += 1; - } - - self.check_terminator(mir, block_data.terminator(), location); - self.check_iscleanup(mir, block_data); - } - } - - fn normalize(&mut self, value: &T, location: Location) -> T - where - T: fmt::Debug + TypeFoldable<'tcx>, - { - self.fully_perform_op(location.at_self(), |this| { - let mut selcx = traits::SelectionContext::new(this.infcx); - let cause = this.misc(this.last_span); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, this.param_env, cause, value); - Ok(InferOk { value, obligations }) - }).unwrap() - } -} - -pub struct TypeckMir; - -impl MirPass for TypeckMir { - fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { - let def_id = src.def_id; - let id = tcx.hir.as_local_node_id(def_id).unwrap(); - debug!("run_pass: {:?}", def_id); - - if tcx.sess.err_count() > 0 { - // compiling a broken program can obviously result in a - // broken MIR, so try not to report duplicate errors. - return; - } - let param_env = tcx.param_env(def_id); - tcx.infer_ctxt().enter(|infcx| { - let _region_constraint_sets = type_check(&infcx, id, param_env, mir); - - // For verification purposes, we just ignore the resulting - // region constraint sets. Not our problem. =) - }); - } -} - -trait AtLocation { - /// Creates a `Locations` where `self` is both the from-location - /// and the at-location. This means that any required region - /// relationships must hold upon entering the statement/terminator - /// indicated by `self`. This is typically used when processing - /// "inputs" to the given location. - fn at_self(self) -> Locations; - - /// Creates a `Locations` where `self` is the from-location and - /// its successor within the block is the at-location. This means - /// that any required region relationships must hold only upon - /// **exiting** the statement/terminator indicated by `self`. This - /// is for example used when you have a `place = rv` statement: it - /// indicates that the `typeof(rv) <: typeof(place)` as of the - /// **next** statement. - fn at_successor_within_block(self) -> Locations; -} - -impl AtLocation for Location { - fn at_self(self) -> Locations { - Locations { - from_location: self, - at_location: self, - } - } - - fn at_successor_within_block(self) -> Locations { - Locations { - from_location: self, - at_location: self.successor_within_block(), - } - } -} -- cgit 1.4.1-3-g733a5 From ebd086b67fe19d9fd861ec26eddf3eee2f734378 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 12:17:20 -0500 Subject: move `LivenessResults` from `nll` into `liveness` analysis --- src/librustc_mir/borrow_check/nll/mod.rs | 25 ++---------------------- src/librustc_mir/util/liveness.rs | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index ffbb3d31917..725694fd5b7 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -16,7 +16,7 @@ use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; use std::io; use transform::MirSource; -use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; +use util::liveness::{LivenessResults, LocalSet}; use borrow_check::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; @@ -86,23 +86,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets); // Compute what is live where. - let liveness = &LivenessResults { - regular: liveness::liveness_of_locals( - &mir, - LivenessMode { - include_regular_use: true, - include_drops: false, - }, - ), - - drop: liveness::liveness_of_locals( - &mir, - LivenessMode { - include_regular_use: false, - include_drops: true, - }, - ), - }; + let liveness = &LivenessResults::compute(mir); // Generate non-subtyping constraints. constraint_generation::generate_constraints( @@ -136,11 +120,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( (regioncx, closure_region_requirements) } -struct LivenessResults { - regular: LivenessResult, - drop: LivenessResult, -} - fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, liveness: &LivenessResults, diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 45c3fcd8a61..5163f74dd25 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -80,6 +80,39 @@ pub struct LivenessMode { pub include_drops: bool, } +/// A combination of liveness results, used in NLL. +pub struct LivenessResults { + /// Liveness results where a regular use makes a variable X live, + /// but not a drop. + pub regular: LivenessResult, + + /// Liveness results where a drop makes a variable X live, + /// but not a regular use. + pub drop: LivenessResult, +} + +impl LivenessResults { + pub fn compute<'tcx>(mir: &Mir<'tcx>) -> LivenessResults { + LivenessResults { + regular: liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: true, + include_drops: false, + }, + ), + + drop: liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: false, + include_drops: true, + }, + ), + } + } +} + /// Compute which local variables are live within the given function /// `mir`. The liveness mode `mode` determines what sorts of uses are /// considered to make a variable live (e.g., do drops count?). -- cgit 1.4.1-3-g733a5 From 4a940b321534a1024078cce92fc69030bfd6c60d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 12:49:08 -0500 Subject: move `flow_in_progress` into `dataflow` and document it --- src/librustc_mir/borrow_check/mod.rs | 4 +-- .../borrow_check/nll/constraint_generation.rs | 2 +- src/librustc_mir/borrow_check/nll/mod.rs | 2 +- src/librustc_mir/dataflow/at_location.rs | 42 +++++++++++++++++----- 4 files changed, 38 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 39bcd2b6ae0..576d5985798 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -22,16 +22,16 @@ use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; use rustc::mir::ClosureRegionRequirements; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_set::{IdxSetBuf}; +use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; use syntax::ast; use syntax_pos::Span; use dataflow::{do_dataflow, DebugFormatted}; +use dataflow::FlowAtLocation; use dataflow::MoveDataParamEnv; use dataflow::{DataflowAnalysis, DataflowResultsConsumer}; -use dataflow::{FlowAtLocation, FlowsAtLocation}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{EverInitializedLvals, MovingOutStatements}; use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex}; diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 59e862a56af..5564a0d98af 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -22,7 +22,7 @@ use rustc::ty::fold::TypeFoldable; use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; -use borrow_check::{FlowAtLocation, FlowsAtLocation}; +use dataflow::{FlowAtLocation, FlowsAtLocation}; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::{HasMoveData, MoveData}; diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 725694fd5b7..c616f027331 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -17,7 +17,7 @@ use std::collections::BTreeSet; use std::io; use transform::MirSource; use util::liveness::{LivenessResults, LocalSet}; -use borrow_check::FlowAtLocation; +use dataflow::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs index a6c3398489a..7f243ad6e26 100644 --- a/src/librustc_mir/dataflow/at_location.rs +++ b/src/librustc_mir/dataflow/at_location.rs @@ -25,26 +25,43 @@ use std::iter; /// There's probably a way to auto-impl this, but I think /// it is cleaner to have manual visitor impls. pub trait FlowsAtLocation { - // reset the state bitvector to represent the entry to block `bb`. + /// Reset the state bitvector to represent the entry to block `bb`. fn reset_to_entry_of(&mut self, bb: BasicBlock); - // build gen + kill sets for statement at `loc`. + /// Build gen + kill sets for statement at `loc`. + /// + /// Note that invoking this method alone does not change the + /// `curr_state` -- you must invoke `apply_local_effect` + /// afterwards. fn reconstruct_statement_effect(&mut self, loc: Location); - // build gen + kill sets for terminator for `loc`. + /// Build gen + kill sets for terminator for `loc`. + /// + /// Note that invoking this method alone does not change the + /// `curr_state` -- you must invoke `apply_local_effect` + /// afterwards. fn reconstruct_terminator_effect(&mut self, loc: Location); - // apply current gen + kill sets to `flow_state`. - // - // (`bb` and `stmt_idx` parameters can be ignored if desired by - // client. For the terminator, the `stmt_idx` will be the number - // of statements in the block.) + /// Apply current gen + kill sets to `flow_state`. + /// + /// (`loc` parameters can be ignored if desired by + /// client. For the terminator, the `stmt_idx` will be the number + /// of statements in the block.) fn apply_local_effect(&mut self, loc: Location); } /// Represents the state of dataflow at a particular /// CFG location, both before and after it is /// executed. +/// +/// Data flow results are typically computed only as basic block +/// boundaries. A `FlowInProgress` allows you to reconstruct the +/// effects at any point in the control-flow graph by starting with +/// the state at the start of the basic block (`reset_to_entry_of`) +/// and then replaying the effects of statements and terminators +/// (e.g. via `reconstruct_statement_effect` and +/// `reconstruct_terminator_effect`; don't forget to call +/// `apply_local_effect`). pub struct FlowAtLocation where BD: BitDenotation, @@ -59,6 +76,7 @@ impl FlowAtLocation where BD: BitDenotation, { + /// Iterate over each bit set in the current state. pub fn each_state_bit(&self, f: F) where F: FnMut(BD::Idx), @@ -67,6 +85,9 @@ where .each_bit(self.base_results.operator().bits_per_block(), f) } + /// Iterate over each `gen` bit in the current effect (invoke + /// `reconstruct_statement_effect` or + /// `reconstruct_terminator_effect` first). pub fn each_gen_bit(&self, f: F) where F: FnMut(BD::Idx), @@ -88,6 +109,7 @@ where } } + /// Access the underlying operator. pub fn operator(&self) -> &BD { self.base_results.operator() } @@ -96,11 +118,15 @@ where self.curr_state.contains(x) } + /// Returns an iterator over the elements present in the current state. pub fn elems_incoming(&self) -> iter::Peekable> { let univ = self.base_results.sets().bits_per_block(); self.curr_state.elems(univ).peekable() } + /// Creates a clone of the current state and applies the local + /// effects to the clone (leaving the state of self intact). + /// Invokes `f` with an iterator over the resulting state. pub fn with_elems_outgoing(&self, f: F) where F: FnOnce(indexed_set::Elems), -- cgit 1.4.1-3-g733a5 From 47c1921b9adb7c6e783ef4ec2a5f1dbe312b3da7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 13:14:36 -0500 Subject: move some parts of liveness to happen during type checking This allows us to re-use the `normalize` method on `TypeCheck`, which is important since normalization may create fresh region variables. This is not an ideal solution, though, since the current representation of "liveness constraints" (a vector of (region, point) pairs) is rather inefficient. Could do somewhat better by converting to indices, but it'd still be less good than the older code. Unclear how important this is. --- .../borrow_check/nll/constraint_generation.rs | 173 +--------------- src/librustc_mir/borrow_check/nll/mod.rs | 23 ++- .../borrow_check/nll/region_infer/mod.rs | 18 +- .../borrow_check/nll/type_check/liveness.rs | 220 +++++++++++++++++++++ .../borrow_check/nll/type_check/mod.rs | 37 +++- src/test/ui/nll/ty-outlives/projection-fn.rs | 53 +++++ src/test/ui/nll/ty-outlives/projection-fn.stderr | 26 +++ 7 files changed, 351 insertions(+), 199 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/liveness.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-fn.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-fn.stderr (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 5564a0d98af..673e85e6b61 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -12,21 +12,13 @@ use rustc::hir; use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Place::Projection; -use rustc::mir::{Local, PlaceProjection, ProjectionElem}; +use rustc::mir::{PlaceProjection, ProjectionElem}; use rustc::mir::visit::TyContext; use rustc::infer::InferCtxt; -use rustc::traits::{self, ObligationCause}; -use rustc::ty::{self, ClosureSubsts, Ty}; +use rustc::ty::{self, ClosureSubsts}; use rustc::ty::subst::Substs; use rustc::ty::fold::TypeFoldable; -use rustc::util::common::ErrorReported; -use rustc_data_structures::fx::FxHashSet; -use syntax::codemap::DUMMY_SP; -use dataflow::{FlowAtLocation, FlowsAtLocation}; -use dataflow::MaybeInitializedLvals; -use dataflow::move_paths::{HasMoveData, MoveData}; -use super::LivenessResults; use super::ToRegionVid; use super::region_infer::RegionInferenceContext; @@ -34,19 +26,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, mir: &Mir<'tcx>, - param_env: ty::ParamEnv<'tcx>, - liveness: &LivenessResults, - flow_inits: &mut FlowAtLocation>, - move_data: &MoveData<'tcx>, ) { let mut cg = ConstraintGeneration { infcx, regioncx, mir, - liveness, - param_env, - flow_inits, - move_data, }; for (bb, data) in mir.basic_blocks().iter_enumerated() { @@ -59,16 +43,10 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>, regioncx: &'cg mut RegionInferenceContext<'tcx>, mir: &'cg Mir<'tcx>, - liveness: &'cg LivenessResults, - param_env: ty::ParamEnv<'tcx>, - flow_inits: &'cg mut FlowAtLocation>, - move_data: &'cg MoveData<'tcx>, } - impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { - self.add_liveness_constraints(bb); self.super_basic_block_data(bb, data); } @@ -130,84 +108,6 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx } impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { - /// Liveness constraints: - /// - /// > If a variable V is live at point P, then all regions R in the type of V - /// > must include the point P. - fn add_liveness_constraints(&mut self, bb: BasicBlock) { - debug!("add_liveness_constraints(bb={:?})", bb); - - self.liveness - .regular - .simulate_block(self.mir, bb, |location, live_locals| { - for live_local in live_locals.iter() { - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_regular_live_constraint(live_local_ty, location); - } - }); - - let mut all_live_locals: Vec<(Location, Vec)> = vec![]; - self.liveness - .drop - .simulate_block(self.mir, bb, |location, live_locals| { - all_live_locals.push((location, live_locals.iter().collect())); - }); - debug!( - "add_liveness_constraints: all_live_locals={:#?}", - all_live_locals - ); - - let terminator_index = self.mir.basic_blocks()[bb].statements.len(); - self.flow_inits.reset_to_entry_of(bb); - while let Some((location, live_locals)) = all_live_locals.pop() { - for live_local in live_locals { - debug!( - "add_liveness_constraints: location={:?} live_local={:?}", - location, - live_local - ); - - self.flow_inits.each_state_bit(|mpi_init| { - debug!( - "add_liveness_constraints: location={:?} initialized={:?}", - location, - &self.flow_inits - .operator() - .move_data() - .move_paths[mpi_init] - ); - }); - - let mpi = self.move_data.rev_lookup.find_local(live_local); - if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { - debug!( - "add_liveness_constraints: mpi={:?} has initialized child {:?}", - self.move_data.move_paths[mpi], - self.move_data.move_paths[initialized_child] - ); - - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_drop_live_constraint(live_local_ty, location); - } - } - - if location.statement_index == terminator_index { - debug!( - "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", - location - ); - self.flow_inits.reconstruct_terminator_effect(location); - } else { - debug!( - "add_liveness_constraints: reconstruct_statement_effect from {:#?}", - location - ); - self.flow_inits.reconstruct_statement_effect(location); - } - self.flow_inits.apply_local_effect(location); - } - } - /// Some variable with type `live_ty` is "regular live" at /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at @@ -230,75 +130,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { }); } - /// Some variable with type `live_ty` is "drop live" at `location` - /// -- i.e., it may be dropped later. This means that *some* of - /// the regions in its type must be live at `location`. The - /// precise set will depend on the dropck constraints, and in - /// particular this takes `#[may_dangle]` into account. - fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { - debug!( - "add_drop_live_constraint(dropped_ty={:?}, location={:?})", - dropped_ty, - location - ); - - let tcx = self.infcx.tcx; - let mut types = vec![(dropped_ty, 0)]; - let mut known = FxHashSet(); - while let Some((ty, depth)) = types.pop() { - let span = DUMMY_SP; // FIXME - let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { - Ok(result) => result, - Err(ErrorReported) => { - continue; - } - }; - - let ty::DtorckConstraint { - outlives, - dtorck_types, - } = result; - - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - for outlive in outlives { - self.add_regular_live_constraint(outlive, location); - } - - // However, there may also be some types that - // `dtorck_constraint_for_ty` could not resolve (e.g., - // associated types and parameters). We need to normalize - // associated types here and possibly recursively process. - for ty in dtorck_types { - let cause = ObligationCause::dummy(); - // We know that our original `dropped_ty` is well-formed, - // so region obligations resulting from this normalization - // should always hold. - // - // Therefore we ignore them instead of trying to match - // them up with a location. - let fulfillcx = traits::FulfillmentContext::new_ignoring_regions(); - match traits::fully_normalize_with_fulfillcx( - self.infcx, fulfillcx, cause, self.param_env, &ty - ) { - Ok(ty) => match ty.sty { - ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { - self.add_regular_live_constraint(ty, location); - } - - _ => if known.insert(ty) { - types.push((ty, depth + 1)); - }, - }, - - Err(errors) => { - self.infcx.report_fulfillment_errors(&errors, None); - } - } - } - } - } - fn add_reborrow_constraint( &mut self, location: Location, diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index c616f027331..3329448e2b7 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -77,7 +77,16 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( ) { // Run the MIR type-checker. let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); - let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir); + let liveness = &LivenessResults::compute(mir); + let constraint_sets = &type_check::type_check( + infcx, + mir_node_id, + param_env, + mir, + &liveness, + flow_inits, + move_data, + ); // Create the region inference context, taking ownership of the region inference // data that was contained in `infcx`. @@ -85,19 +94,9 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir); subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets); - // Compute what is live where. - let liveness = &LivenessResults::compute(mir); // Generate non-subtyping constraints. - constraint_generation::generate_constraints( - infcx, - &mut regioncx, - &mir, - param_env, - liveness, - flow_inits, - move_data, - ); + constraint_generation::generate_constraints(infcx, &mut regioncx, &mir); // Solve the region constraints. let closure_region_requirements = regioncx.solve(infcx, &mir, def_id); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 86ba7524bf0..f36392c9fcf 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -425,11 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// therefore add `end('a)` into the region for `'b` -- but we /// have no evidence that `'b` outlives `'a`, so we want to report /// an error. - fn check_type_tests( - &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - mir: &Mir<'tcx>, - ) { + fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -473,13 +469,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { .iter() .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), - RegionTest::Any(tests) => tests - .iter() - .any(|test| self.eval_region_test(mir, point, lower_bound, test)), + RegionTest::Any(tests) => tests.iter().any(|test| { + self.eval_region_test(mir, point, lower_bound, test) + }), - RegionTest::All(tests) => tests - .iter() - .all(|test| self.eval_region_test(mir, point, lower_bound, test)), + RegionTest::All(tests) => tests.iter().all(|test| { + self.eval_region_test(mir, point, lower_bound, test) + }), } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs new file mode 100644 index 00000000000..e41bf7cda8e --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -0,0 +1,220 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use dataflow::{FlowAtLocation, FlowsAtLocation}; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::{HasMoveData, MoveData}; +use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::mir::Local; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::util::common::ErrorReported; +use rustc_data_structures::fx::FxHashSet; +use syntax::codemap::DUMMY_SP; +use util::liveness::LivenessResults; + +use super::TypeChecker; + +/// Combines liveness analysis with initialization analysis to +/// determine which variables are live at which points, both due to +/// ordinary uses and drops. Returns a set of (ty, location) pairs +/// that indicate which types must be live at which point in the CFG. +/// This vector is consumed by `constraint_generation`. +/// +/// NB. This computation requires normalization; therefore, it must be +/// performed before +pub(super) fn generate<'gcx, 'tcx>( + cx: &mut TypeChecker<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation>, + move_data: &MoveData<'tcx>, +) { + let tcx = cx.tcx(); + let mut generator = TypeLivenessGenerator { + cx, + tcx, + mir, + liveness, + flow_inits, + move_data, + }; + + for bb in mir.basic_blocks().indices() { + generator.add_liveness_constraints(bb); + } +} + +struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> +where + 'typeck: 'gen, + 'flow: 'gen, + 'tcx: 'typeck + 'flow, + 'gcx: 'tcx, +{ + cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, + tcx: TyCtxt<'typeck, 'gcx, 'tcx>, + mir: &'gen Mir<'tcx>, + liveness: &'gen LivenessResults, + flow_inits: &'gen mut FlowAtLocation>, + move_data: &'gen MoveData<'tcx>, +} + +impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { + /// Liveness constraints: + /// + /// > If a variable V is live at point P, then all regions R in the type of V + /// > must include the point P. + fn add_liveness_constraints(&mut self, bb: BasicBlock) { + debug!("add_liveness_constraints(bb={:?})", bb); + + self.liveness + .regular + .simulate_block(self.mir, bb, |location, live_locals| { + for live_local in live_locals.iter() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.push_type_live_constraint(live_local_ty, location); + } + }); + + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; + self.liveness + .drop + .simulate_block(self.mir, bb, |location, live_locals| { + all_live_locals.push((location, live_locals.iter().collect())); + }); + debug!( + "add_liveness_constraints: all_live_locals={:#?}", + all_live_locals + ); + + let terminator_index = self.mir.basic_blocks()[bb].statements.len(); + self.flow_inits.reset_to_entry_of(bb); + while let Some((location, live_locals)) = all_live_locals.pop() { + for live_local in live_locals { + debug!( + "add_liveness_constraints: location={:?} live_local={:?}", + location, + live_local + ); + + self.flow_inits.each_state_bit(|mpi_init| { + debug!( + "add_liveness_constraints: location={:?} initialized={:?}", + location, + &self.flow_inits.operator().move_data().move_paths[mpi_init] + ); + }); + + let mpi = self.move_data.rev_lookup.find_local(live_local); + if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { + debug!( + "add_liveness_constraints: mpi={:?} has initialized child {:?}", + self.move_data.move_paths[mpi], + self.move_data.move_paths[initialized_child] + ); + + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); + } + } + + if location.statement_index == terminator_index { + debug!( + "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_terminator_effect(location); + } else { + debug!( + "add_liveness_constraints: reconstruct_statement_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_statement_effect(location); + } + self.flow_inits.apply_local_effect(location); + } + } + + /// Some variable with type `live_ty` is "regular live" at + /// `location` -- i.e., it may be used later. This means that all + /// regions appearing in the type `live_ty` must be live at + /// `location`. + fn push_type_live_constraint(&mut self, value: T, location: Location) + where + T: TypeFoldable<'tcx>, + { + debug!( + "push_type_live_constraint(live_ty={:?}, location={:?})", + value, + location + ); + + self.tcx.for_each_free_region(&value, |live_region| { + self.cx + .constraints + .liveness_set + .push((live_region, location)); + }); + } + + /// Some variable with type `live_ty` is "drop live" at `location` + /// -- i.e., it may be dropped later. This means that *some* of + /// the regions in its type must be live at `location`. The + /// precise set will depend on the dropck constraints, and in + /// particular this takes `#[may_dangle]` into account. + fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { + debug!( + "add_drop_live_constraint(dropped_ty={:?}, location={:?})", + dropped_ty, + location + ); + + let tcx = self.cx.infcx.tcx; + let mut types = vec![(dropped_ty, 0)]; + let mut known = FxHashSet(); + while let Some((ty, depth)) = types.pop() { + let span = DUMMY_SP; // FIXME + let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { + Ok(result) => result, + Err(ErrorReported) => { + continue; + } + }; + + let ty::DtorckConstraint { + outlives, + dtorck_types, + } = result; + + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. + for outlive in outlives { + self.push_type_live_constraint(outlive, location); + } + + // However, there may also be some types that + // `dtorck_constraint_for_ty` could not resolve (e.g., + // associated types and parameters). We need to normalize + // associated types here and possibly recursively process. + for ty in dtorck_types { + let ty = self.cx.normalize(&ty, location); + match ty.sty { + ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { + self.push_type_live_constraint(ty, location); + } + + _ => if known.insert(ty) { + types.push((ty, depth + 1)); + }, + } + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index fae911780a1..8f18105600a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -12,6 +12,9 @@ #![allow(unreachable_code)] use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; +use dataflow::FlowAtLocation; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::MoveData; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::region_constraints::RegionConstraintData; use rustc::traits::{self, FulfillmentContext}; @@ -26,22 +29,43 @@ use std::fmt; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; +use util::liveness::LivenessResults; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +mod liveness; + /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to -/// be proven. +/// be proven. This result is includes liveness constraints that +/// ensure that regions appearing in the types of all local variables +/// are live at all points where that local variable may later be +/// used. /// /// This phase of type-check ought to be infallible -- this is because /// the original, HIR-based type-check succeeded. So if any errors /// occur here, we will get a `bug!` reported. -pub fn type_check<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +pub(crate) fn type_check<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation>, + move_data: &MoveData<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + type_check_internal(infcx, body_id, param_env, mir, &mut |cx| { + liveness::generate(cx, mir, liveness, flow_inits, move_data) + }) +} + +fn type_check_internal<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { let mut checker = TypeChecker::new(infcx, body_id, param_env); let errors_reported = { @@ -55,9 +79,12 @@ pub fn type_check<'a, 'gcx, 'tcx>( checker.typeck_mir(mir); } + extra(&mut checker); + checker.constraints } + fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { // We sometimes see MIR failures (notably predicate failures) due to // the fact that we check rvalue sized predicates here. So use `delay_span_bug` @@ -503,7 +530,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { /// constraints needed for it to be valid and well-typed. Along the /// way, it accrues region constraints -- these can later be used by /// NLL region checking. -pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { +struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, last_span: Span, @@ -1474,7 +1501,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _region_constraint_sets = type_check(&infcx, id, param_env, mir); + let _ = type_check_internal(&infcx, id, param_env, mir, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/test/ui/nll/ty-outlives/projection-fn.rs b/src/test/ui/nll/ty-outlives/projection-fn.rs new file mode 100644 index 00000000000..677d1352788 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-fn.rs @@ -0,0 +1,53 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir + +#![allow(warnings)] +#![feature(dyn_trait)] + +trait Anything { } + +impl Anything for T { } + +fn no_region<'a, T>(mut x: T) -> Box +where + T: Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(mut x: T) -> Box +where + T: 'a + Iterator, +{ + Box::new(x.next()) +} + +fn wrong_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + Box::new(x.next()) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr new file mode 100644 index 00000000000..a478348edc1 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) } + --> $DIR/projection-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 02e65250e4dfb22d5e1bc1e637913cbcfc60db13 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 04:47:59 -0500 Subject: mild refactors of the control flow (no functional changes) In the future, `check_type_tests` will also potentially propagate constriants to its caller. --- .../borrow_check/nll/region_infer/mod.rs | 72 +++++++++++++--------- 1 file changed, 44 insertions(+), 28 deletions(-) (limited to 'src') 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 f36392c9fcf..efa0734b016 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -349,9 +349,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.propagate_constraints(mir); + // If this is a closure, we can propagate unsatisfied + // `outlives_requirements` to our creator, so create a vector + // to store those. Otherwise, we'll pass in `None` to the + // functions below, which will trigger them to report errors + // eagerly. + let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) { + Some(vec![]) + } else { + None + }; + self.check_type_tests(infcx, mir); - let outlives_requirements = self.check_universal_regions(infcx, mir_def_id); + self.check_universal_regions(infcx, outlives_requirements.as_mut()); + + let outlives_requirements = outlives_requirements.unwrap_or(vec![]); if outlives_requirements.is_empty() { None @@ -429,18 +442,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if !self.eval_region_test( + if self.eval_region_test( mir, type_test.point, type_test.lower_bound, &type_test.test, ) { - // Oh the humanity. Obviously we will do better than this error eventually. - infcx.tcx.sess.span_err( - type_test.span, - &format!("failed type test: {:?}", type_test), - ); + continue; } + + // Oh the humanity. Obviously we will do better than this error eventually. + infcx.tcx.sess.span_err( + type_test.span, + &format!("failed type test: {:?}", type_test), + ); } } @@ -538,11 +553,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// therefore add `end('a)` into the region for `'b` -- but we /// have no evidence that `'b` outlives `'a`, so we want to report /// an error. + /// + /// If `propagated_outlives_requirements` is `Some`, then we will + /// push unsatisfied obligations into there. Otherwise, we'll + /// report them as errors. fn check_universal_regions( &self, infcx: &InferCtxt<'_, '_, 'tcx>, - mir_def_id: DefId, - ) -> Vec { + mut propagated_outlives_requirements: Option<&mut Vec>, + ) { // The universal regions are always found in a prefix of the // full list. let universal_definitions = self.definitions @@ -555,25 +574,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut outlives_requirements = vec![]; for (fr, _) in universal_definitions { self.check_universal_region(infcx, fr, &mut outlives_requirements); - } - - // If this is a closure, we can propagate unsatisfied - // `outlives_requirements` to our creator. Otherwise, we have - // to report a hard error here. - if infcx.tcx.is_closure(mir_def_id) { - return outlives_requirements; - } - for outlives_requirement in outlives_requirements { - self.report_error( - infcx, - outlives_requirement.free_region, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); + // Propagate unsatisfied requirements if possible, else + // report them. + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { + propagated_outlives_requirements.extend(outlives_requirements.drain(..)); + } else { + for outlives_requirement in outlives_requirements.drain(..) { + self.report_error( + infcx, + outlives_requirement.free_region, + outlives_requirement.outlived_free_region, + outlives_requirement.blame_span, + ); + } + } } - - vec![] } /// Check the final value for the free region `fr` to see if it @@ -588,7 +604,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, '_, 'tcx>, longer_fr: RegionVid, - outlives_requirements: &mut Vec, + propagated_outlives_requirements: &mut Vec, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -626,7 +642,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); // Push the constraint `fr-: shorter_fr+` - outlives_requirements.push(ClosureOutlivesRequirement { + propagated_outlives_requirements.push(ClosureOutlivesRequirement { free_region: fr_minus, outlived_free_region: shorter_fr_plus, blame_span: blame_span, -- cgit 1.4.1-3-g733a5 From 154cd9419763f35c0f38b1cb044f43eeece1ebf0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 05:11:36 -0500 Subject: rework region flags: 'static can be erased too The existing flags did not consider `'static` to be "free". This then fed into what was "erasable" -- but `'static` is most certainly erasable. --- src/librustc/ty/fold.rs | 15 ++++++++++----- src/librustc/ty/mod.rs | 10 ++++++++++ src/librustc/ty/sty.rs | 17 ++++++++++++++--- 3 files changed, 34 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 069dc0275cb..c5b82730e48 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -97,14 +97,19 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn has_closure_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_CLOSURE) } + /// "Free" regions in this context means that it has any region + /// that is not (a) erased or (b) late-bound. + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + /// True if there any any un-erased free regions. fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | - TypeFlags::HAS_FREE_REGIONS) + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) } + fn is_normalized_for_trans(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | + !self.has_type_flags(TypeFlags::HAS_RE_INFER | TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_TY_INFER | TypeFlags::HAS_PARAMS | diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4e1a79d4613..9d088b95bbb 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -421,8 +421,18 @@ bitflags! { const HAS_TY_INFER = 1 << 2; const HAS_RE_INFER = 1 << 3; const HAS_RE_SKOL = 1 << 4; + + /// Does this have any `ReEarlyBound` regions? Used to + /// determine whether substitition is required, since those + /// represent regions that are bound in a `ty::Generics` and + /// hence may be substituted. const HAS_RE_EARLY_BOUND = 1 << 5; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but `ReLateBound` and `ReErased`. const HAS_FREE_REGIONS = 1 << 6; + + /// Is an error type reachable? const HAS_TY_ERR = 1 << 7; const HAS_PROJECTION = 1 << 8; diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index e085d1311c3..2f72b3dde42 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1184,18 +1184,29 @@ impl RegionKind { match *self { ty::ReVar(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; } ty::ReSkolemized(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; flags = flags | TypeFlags::HAS_RE_SKOL; flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; } ty::ReLateBound(..) => { } - ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; } - ty::ReStatic | ty::ReErased => { } - _ => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } + ty::ReEarlyBound(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; + } + ty::ReEmpty | + ty::ReStatic | + ty::ReFree { .. } | + ty::ReScope { .. } => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } + ty::ReErased => { + } } match *self { -- cgit 1.4.1-3-g733a5 From c7cfa2367b0ad4aed16dff7ad3d8d3e9eea6540a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 05:40:43 -0500 Subject: thread through an implicit region body of the fn body --- src/librustc_mir/borrow_check/nll/mod.rs | 2 ++ .../borrow_check/nll/region_infer/mod.rs | 4 ++- .../borrow_check/nll/type_check/mod.rs | 28 +++++++++++---- .../borrow_check/nll/universal_regions.rs | 15 ++++++-- src/test/mir-opt/nll/named-lifetimes-basic.rs | 10 +++--- src/test/mir-opt/nll/reborrow-basic.rs | 8 ++--- src/test/mir-opt/nll/region-liveness-basic.rs | 8 ++--- .../mir-opt/nll/region-liveness-drop-may-dangle.rs | 4 ++- .../nll/region-liveness-drop-no-may-dangle.rs | 4 ++- .../nll/region-liveness-two-disjoint-uses.rs | 12 +++---- src/test/mir-opt/nll/region-subtyping-basic.rs | 12 +++---- src/test/ui/nll/capture-ref-in-struct.stderr | 2 +- .../closure-requirements/escape-argument.stderr | 2 +- .../escape-upvar-nested.stderr | 2 +- .../closure-requirements/escape-upvar-ref.stderr | 2 +- ...shorter-to-static-comparing-against-free.stderr | 2 +- src/test/ui/nll/ty-outlives/projection-fn.stderr | 4 +-- src/test/ui/nll/ty-outlives/ty-param-fn-body.rs | 41 ++++++++++++++++++++++ .../ui/nll/ty-outlives/ty-param-fn-body.stderr | 14 ++++++++ src/test/ui/nll/ty-outlives/ty-param-fn.stderr | 4 +-- 20 files changed, 135 insertions(+), 45 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn-body.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 3329448e2b7..9e7c94cd7ff 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -78,11 +78,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( // Run the MIR type-checker. let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); let liveness = &LivenessResults::compute(mir); + let fr_fn_body = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let constraint_sets = &type_check::type_check( infcx, mir_node_id, param_env, mir, + fr_fn_body, &liveness, flow_inits, move_data, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index efa0734b016..4759185d226 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -439,6 +439,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// have no evidence that `'b` outlives `'a`, so we want to report /// an error. fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { + let tcx = infcx.tcx; + for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -452,7 +454,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Oh the humanity. Obviously we will do better than this error eventually. - infcx.tcx.sess.span_err( + tcx.sess.span_err( type_test.span, &format!("failed type test: {:?}", type_test), ); diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 8f18105600a..f7493f653e0 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -51,13 +51,19 @@ pub(crate) fn type_check<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + implicit_region_bound: ty::Region<'tcx>, liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) -> MirTypeckRegionConstraints<'tcx> { - type_check_internal(infcx, body_id, param_env, mir, &mut |cx| { - liveness::generate(cx, mir, liveness, flow_inits, move_data) - }) + type_check_internal( + infcx, + body_id, + param_env, + mir, + Some(implicit_region_bound), + &mut |cx| liveness::generate(cx, mir, liveness, flow_inits, move_data), + ) } fn type_check_internal<'gcx, 'tcx>( @@ -65,9 +71,10 @@ fn type_check_internal<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + implicit_region_bound: Option>, extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { - let mut checker = TypeChecker::new(infcx, body_id, param_env); + let mut checker = TypeChecker::new(infcx, body_id, param_env, implicit_region_bound); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); verifier.visit_mir(mir); @@ -535,6 +542,7 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'gcx>, last_span: Span, body_id: ast::NodeId, + implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, constraints: MirTypeckRegionConstraints<'tcx>, } @@ -588,12 +596,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, + implicit_region_bound: Option>, ) -> Self { TypeChecker { infcx, last_span: DUMMY_SP, body_id, param_env, + implicit_region_bound, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), } @@ -618,8 +628,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { span_mirbug!(self, "", "errors selecting obligation: {:?}", e); } - self.infcx - .process_registered_region_obligations(&[], None, self.param_env, self.body_id); + self.infcx.process_registered_region_obligations( + &[], + self.implicit_region_bound, + self.param_env, + self.body_id, + ); let data = self.infcx.take_and_reset_region_constraints(); if !data.is_empty() { @@ -1501,7 +1515,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _ = type_check_internal(&infcx, id, param_env, mir, &mut |_| ()); + let _ = type_check_internal(&infcx, id, param_env, mir, None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5336bd271f5..ce931a68a0c 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -45,6 +45,11 @@ pub struct UniversalRegions<'tcx> { /// The vid assigned to `'static` pub fr_static: RegionVid, + /// A special region vid created to represent the current MIR fn + /// body. It will outlive the entire CFG but it will not outlive + /// any other universal regions. + pub fr_fn_body: RegionVid, + /// We create region variables such that they are ordered by their /// `RegionClassification`. The first block are globals, then /// externals, then locals. So things from: @@ -408,6 +413,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { let first_local_index = self.infcx.num_region_vars(); let inputs_and_output = self.infcx .replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output); + let fr_fn_body = self.infcx.next_nll_region_var(FR).to_region_vid(); let num_universals = self.infcx.num_region_vars(); // Insert the facts we know from the predicates. Why? Why not. @@ -419,12 +425,16 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { self.add_implied_bounds(&indices, ty); } - // Finally, outlives is reflexive, and static outlives every - // other free region. + // Finally: + // - outlives is reflexive, so `'r: 'r` for every region `'r` + // - `'static: 'r` for every region `'r` + // - `'r: 'fn_body` for every (other) universally quantified + // region `'r`, all of which are provided by our caller for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) { debug!("build: relating free region {:?} to itself and to 'static", fr); self.relations.relate_universal_regions(fr, fr); self.relations.relate_universal_regions(fr_static, fr); + self.relations.relate_universal_regions(fr, fr_fn_body); } let (output_ty, input_tys) = inputs_and_output.split_last().unwrap(); @@ -445,6 +455,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { UniversalRegions { indices, fr_static, + fr_fn_body, first_extern_index, first_local_index, num_universals, diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index f3a57c08840..71304f71b61 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -27,16 +27,18 @@ fn main() { // END RUST SOURCE // START rustc.use_x.nll.0.mir // | Free Region Mapping -// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#3r] -// | '_#1r | External | ['_#1r] -// | '_#2r | External | ['_#2r, '_#1r] -// | '_#3r | Local | ['_#3r] +// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] +// | '_#1r | External | ['_#1r, '_#4r] +// | '_#2r | External | ['_#2r, '_#1r, '_#4r] +// | '_#3r | Local | ['_#4r, '_#3r] +// | '_#4r | Local | ['_#4r] // | // | Inferred Region Values // | '_#0r | {'_#0r, bb0[0], bb0[1]} // | '_#1r | {'_#1r, bb0[0], bb0[1]} // | '_#2r | {'_#2r, bb0[0], bb0[1]} // | '_#3r | {'_#3r, bb0[0], bb0[1]} +// | '_#4r | {'_#4r, bb0[0], bb0[1]} // | // ... // fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index d203472f20c..9e6d6aaee15 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,11 +28,11 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#6r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#7r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// | '_#8r | {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#9r | {bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// let _2: &'_#6r mut i32; +// let _2: &'_#7r mut i32; // ... -// let _4: &'_#8r mut i32; +// let _4: &'_#9r mut i32; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index c04cedbc04b..ab99d79d7f3 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,15 +31,15 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]} -// | '_#2r | {bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#3r | {bb2[1], bb3[0], bb3[1]} // ... -// let _2: &'_#2r usize; +// let _2: &'_#3r usize; // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // bb2: { // | Live variables on entry to bb2[0]: [_1, _3] -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // | Live variables on entry to bb2[1]: [_2] // switchInt(const true) -> [0u8: bb4, otherwise: bb3]; // } diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index e2ad49a4436..515772a9427 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,5 +44,7 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#5r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// ... +// let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index e0272a51d03..a257910b0b8 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,5 +46,7 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#5r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// ... +// let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index 8aa0eb1a3a9..3041c2cb061 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -36,14 +36,14 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} // ... -// | '_#3r | {bb8[1], bb8[2], bb8[3], bb8[4]} -// | '_#4r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} +// | '_#4r | {bb8[1], bb8[2], bb8[3], bb8[4]} +// | '_#5r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} // ... -// let mut _2: &'_#4r usize; +// let mut _2: &'_#5r usize; // ... -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // ... -// _2 = &'_#3r (*_10); +// _2 = &'_#4r (*_10); // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 2bc165bd3c4..5a3f831331c 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,16 +32,16 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} -// | '_#2r | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} -// | '_#3r | {bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#2r | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#3r | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#4r | {bb2[5], bb2[6], bb3[0], bb3[1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#2r usize; +// let _2: &'_#3r usize; // ... -// let _6: &'_#3r usize; +// let _6: &'_#4r usize; // ... -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // ... // _7 = _2; // ... diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr index 6b57f91987b..9f62b2a41c5 100644 --- a/src/test/ui/nll/capture-ref-in-struct.stderr +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -7,7 +7,7 @@ error[E0597]: `y` does not live long enough 37 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#4r... + = note: borrowed value must be valid for lifetime '_#5r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 567ed299ac1..2b0e3661376 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -32,7 +32,7 @@ error[E0597]: `y` does not live long enough 39 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#5r... + = note: borrowed value must be valid for lifetime '_#6r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 6c70afa0c9c..ddda72c5686 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -59,7 +59,7 @@ error[E0597]: `y` does not live long enough 36 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#3r... + = note: borrowed value must be valid for lifetime '_#4r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 0b982dd812b..a0814cfc15f 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -36,7 +36,7 @@ error[E0597]: `y` does not live long enough 36 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#3r... + = note: borrowed value must be valid for lifetime '_#4r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 3e54e62d011..c5107322f6f 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -79,7 +79,7 @@ error[E0597]: `a` does not live long enough 49 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#1r... + = note: borrowed value must be valid for lifetime '_#2r... error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr index a478348edc1..77eabef6545 100644 --- a/src/test/ui/nll/ty-outlives/projection-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) } +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } --> $DIR/projection-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } --> $DIR/projection-fn.rs:40:5 | 40 | Box::new(x.next()) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs new file mode 100644 index 00000000000..42e62b23896 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -0,0 +1,41 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir + +// Test that we assume that universal types like `T` outlive the +// function body. + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::cell::Cell; + +// No errors here, because `'a` is local to the body. +fn region_within_body(t: T) { + let some_int = 22; + let cell = Cell::new(&some_int); + outlives(cell, t) +} + +// Error here, because T: 'a is not satisfied. +fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { + outlives(cell, t) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives<'a, T>(x: Cell<&'a usize>, y: T) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr new file mode 100644 index 00000000000..3cc335bb29d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn-body.rs:30:5 + | +30 | outlives(cell, t) + | ^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#4r, point: bb0[4], span: $DIR/ty-param-fn-body.rs:30:5: 30:22, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-fn-body.rs:30:5 + | +30 | outlives(cell, t) + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr index 5b29ff88621..426bece3e9d 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 38 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#2r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn([]) } +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } --> $DIR/ty-param-fn.rs:22:5 | 22 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } --> $DIR/ty-param-fn.rs:38:5 | 38 | x -- cgit 1.4.1-3-g733a5 From 5804637a81a8e5216fbbf2e3e7c0a7bdca9c99de Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 11:37:34 -0500 Subject: permit `ClosureOutlivesRequirement` to constrain regions or types --- src/librustc/ich/impls_mir.rs | 21 ++++++-- src/librustc/mir/mod.rs | 33 +++++++++--- src/librustc/ty/maps/mod.rs | 2 +- src/librustc_mir/borrow_check/mod.rs | 4 +- src/librustc_mir/borrow_check/nll/mod.rs | 11 ++-- .../borrow_check/nll/region_infer/mod.rs | 63 ++++++++++++++-------- 6 files changed, 96 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 32577ac46f3..df67c3abbe8 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -536,14 +536,29 @@ impl<'gcx> HashStable> for mir::Literal<'gcx> { impl_stable_hash_for!(struct mir::Location { block, statement_index }); -impl_stable_hash_for!(struct mir::ClosureRegionRequirements { +impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> { num_external_vids, outlives_requirements }); -impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement { - free_region, +impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> { + subject, outlived_free_region, blame_span }); +impl<'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + mir::ClosureOutlivesSubject::Ty(ref ty) => { + ty.hash_stable(hcx, hasher); + } + mir::ClosureOutlivesSubject::Region(ref region) => { + region.hash_stable(hcx, hasher); + } + } + } +} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index a83c3f29d25..d7afce7de46 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1832,7 +1832,7 @@ pub struct GeneratorLayout<'tcx> { /// can be extracted from its type and constrained to have the given /// outlives relationship. #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub struct ClosureRegionRequirements { +pub struct ClosureRegionRequirements<'gcx> { /// The number of external regions defined on the closure. In our /// example above, it would be 3 -- one for `'static`, then `'1` /// and `'2`. This is just used for a sanity check later on, to @@ -1842,15 +1842,15 @@ pub struct ClosureRegionRequirements { /// Requirements between the various free regions defined in /// indices. - pub outlives_requirements: Vec, + pub outlives_requirements: Vec>, } -/// Indicates an outlives constraint between two free-regions declared -/// on the closure. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -pub struct ClosureOutlivesRequirement { - // This region ... - pub free_region: ty::RegionVid, +/// Indicates an outlives constraint between a type or between two +/// free-regions declared on the closure. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ClosureOutlivesRequirement<'tcx> { + // This region or type ... + pub subject: ClosureOutlivesSubject<'tcx>, // .. must outlive this one. pub outlived_free_region: ty::RegionVid, @@ -1859,6 +1859,23 @@ pub struct ClosureOutlivesRequirement { pub blame_span: Span, } +/// The subject of a ClosureOutlivesRequirement -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum ClosureOutlivesSubject<'tcx> { + /// Subject is a type, typically a type parameter, but could also + /// be a projection. Indicates a requirement like `T: 'a` being + /// passed to the caller, where the type here is `T`. + /// + /// The type here is guaranteed not to contain any free regions at + /// present. + Ty(Ty<'tcx>), + + /// Subject is a free region from the closure. Indicates a requirement + /// like `'a: 'b` being passed to the caller; the region here is `'a`. + Region(ty::RegionVid), +} + /* * TypeFoldable implementations for MIR types */ diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 7ba063adff4..319f63dd7c8 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -193,7 +193,7 @@ define_maps! { <'tcx> /// Borrow checks the function body. If this is a closure, returns /// additional requirements that the closure's creator must verify. - [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option, + [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option>, /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 576d5985798..e0b03aec69a 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -65,7 +65,7 @@ pub fn provide(providers: &mut Providers) { fn mir_borrowck<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, -) -> Option { +) -> Option> { let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); @@ -89,7 +89,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, input_mir: &Mir<'gcx>, def_id: DefId, -) -> Option { +) -> Option> { let tcx = infcx.tcx; let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 9e7c94cd7ff..23fa2689053 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -9,11 +9,12 @@ // except according to those terms. use rustc::hir::def_id::DefId; -use rustc::mir::{ClosureRegionRequirements, Mir}; +use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir}; use rustc::infer::InferCtxt; use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; +use std::fmt::Debug; use std::io; use transform::MirSource; use util::liveness::{LivenessResults, LocalSet}; @@ -73,7 +74,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( move_data: &MoveData<'tcx>, ) -> ( RegionInferenceContext<'tcx>, - Option, + Option>, ) { // Run the MIR type-checker. let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); @@ -263,9 +264,13 @@ fn for_each_region_constraint( with_msg: &mut FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for req in &closure_region_requirements.outlives_requirements { + let subject: &Debug = match &req.subject { + ClosureOutlivesSubject::Region(subject) => subject, + ClosureOutlivesSubject::Ty(ty) => ty, + }; with_msg(&format!( "where {:?}: {:?}", - req.free_region, + subject, req.outlived_free_region, ))?; } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 4759185d226..4a1eca38bc7 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -15,7 +15,8 @@ use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; use rustc::infer::region_constraints::{GenericKind, VarOrigins}; -use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; +use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, + Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt; @@ -339,12 +340,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Perform region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. - pub(super) fn solve( + pub(super) fn solve<'gcx>( &mut self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, mir_def_id: DefId, - ) -> Option { + ) -> Option> { assert!(self.inferred_values.is_none(), "values already inferred"); self.propagate_constraints(mir); @@ -559,10 +560,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// If `propagated_outlives_requirements` is `Some`, then we will /// push unsatisfied obligations into there. Otherwise, we'll /// report them as errors. - fn check_universal_regions( + fn check_universal_regions<'gcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - mut propagated_outlives_requirements: Option<&mut Vec>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, ) { // The universal regions are always found in a prefix of the // full list. @@ -583,9 +584,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.extend(outlives_requirements.drain(..)); } else { for outlives_requirement in outlives_requirements.drain(..) { + let fr = match outlives_requirement.subject { + ClosureOutlivesSubject::Region(fr) => fr, + _ => span_bug!( + outlives_requirement.blame_span, + "check_universal_region() produced requirement w/ non-region subject" + ), + }; + self.report_error( infcx, - outlives_requirement.free_region, + fr, outlives_requirement.outlived_free_region, outlives_requirement.blame_span, ); @@ -602,11 +611,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Things that are to be propagated are accumulated into the /// `outlives_requirements` vector. - fn check_universal_region( + fn check_universal_region<'gcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, longer_fr: RegionVid, - propagated_outlives_requirements: &mut Vec, + propagated_outlives_requirements: &mut Vec>, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -645,7 +654,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Push the constraint `fr-: shorter_fr+` propagated_outlives_requirements.push(ClosureOutlivesRequirement { - free_region: fr_minus, + subject: ClosureOutlivesSubject::Region(fr_minus), outlived_free_region: shorter_fr_plus, blame_span: blame_span, }); @@ -773,7 +782,7 @@ pub trait ClosureRegionRequirementsExt { ); } -impl ClosureRegionRequirementsExt for ClosureRegionRequirements { +impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of @@ -815,17 +824,29 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { // Create the predicates. for outlives_requirement in &self.outlives_requirements { - let region = closure_mapping[outlives_requirement.free_region]; let outlived_region = closure_mapping[outlives_requirement.outlived_free_region]; - debug!( - "apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}", - region, - outlived_region, - outlives_requirement - ); + // FIXME, this origin is not entirely suitable. let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span); - infcx.sub_regions(origin, outlived_region, region); + + match outlives_requirement.subject { + ClosureOutlivesSubject::Region(region) => { + let region = closure_mapping[region]; + debug!( + "apply_requirements: region={:?} \ + outlived_region={:?} \ + outlives_requirements={:?}", + region, + outlived_region, + outlives_requirement + ); + infcx.sub_regions(origin, outlived_region, region); + } + + ClosureOutlivesSubject::Ty(_ty) => { + bug!("TODO not yet implemented -- closure outlives subject of a type"); + } + } } } } -- cgit 1.4.1-3-g733a5 From 85e1d4749e5ba14ff89073f5812974ec8fdbffd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 13:04:38 -0500 Subject: propagate type tests from closure to closure creators Currently, we only propagate type tests that exclude all regions from the type. --- .../borrow_check/nll/region_infer/mod.rs | 157 ++++++++++++++--- .../borrow_check/nll/type_check/mod.rs | 1 + .../borrow_check/nll/universal_regions.rs | 43 +++-- .../ty-param-closure-outlives-from-return-type.rs | 66 +++++++ ...-param-closure-outlives-from-return-type.stderr | 58 +++++++ .../ty-param-closure-outlives-from-where-clause.rs | 96 +++++++++++ ...param-closure-outlives-from-where-clause.stderr | 191 +++++++++++++++++++++ 7 files changed, 581 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs create mode 100644 src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr (limited to 'src') 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 4a1eca38bc7..244abb40798 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -12,15 +12,18 @@ use super::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; +use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Location, Mir}; -use rustc::ty::{self, RegionVid}; +use rustc::traits::ObligationCause; +use rustc::ty::{self, RegionVid, TypeFoldable}; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt; use std::rc::Rc; +use syntax::ast; use syntax_pos::Span; mod annotation; @@ -361,7 +364,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { None }; - self.check_type_tests(infcx, mir); + self.check_type_tests(infcx, mir, outlives_requirements.as_mut()); self.check_universal_regions(infcx, outlives_requirements.as_mut()); @@ -439,21 +442,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// therefore add `end('a)` into the region for `'b` -- but we /// have no evidence that `'b` outlives `'a`, so we want to report /// an error. - fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { + fn check_type_tests<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, + ) { let tcx = infcx.tcx; for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if self.eval_region_test( - mir, - type_test.point, - type_test.lower_bound, - &type_test.test, - ) { + if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) { continue; } + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { + if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) { + continue; + } + } + // Oh the humanity. Obviously we will do better than this error eventually. tcx.sess.span_err( type_test.span, @@ -462,6 +471,103 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + fn try_promote_type_test<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + type_test: &TypeTest<'tcx>, + propagated_outlives_requirements: &mut Vec>, + ) -> bool { + let tcx = infcx.tcx; + let gcx = tcx.global_tcx(); + + let TypeTest { + generic_kind, + lower_bound, + point: _, + span, + test: _, + } = type_test; + + // TODO. For now, just fail to promote anything with a + // region. This is obviously too strict: we will for example + // fail to promote `>::Bar` to our + // caller. But it is always sound not to promote, that just + // means more errors, and ignoring regions is a convenient + // starting point. This is because we would want to promote to + // a type that references the region-vids of the closure, for + // which we have no global representation just now. + let generic_ty = generic_kind.to_ty(tcx); + if generic_ty.has_free_regions() { + return false; + } + let generic_ty = gcx.lift(&generic_ty).unwrap(); + + // Find some bounding subject-region R+ that is a super-region + // of the existing subject-region R. This should be a non-local, universal + // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`. + let lower_bound_plus = self.promoted_type_test_bound(*lower_bound); + assert!(self.universal_regions.is_universal_region(lower_bound_plus)); + assert!(!self.universal_regions + .is_local_free_region(lower_bound_plus)); + + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Ty(generic_ty), + outlived_free_region: lower_bound_plus, + blame_span: *span, + }); + true + } + + /// Here, `lower_bound` (henceforth, `'r`) represents the bound from + /// some type-test `T: 'r`. We are a closure and have found that + /// `T: 'r` is not locally satisfiable, so we want to propagate + /// this constraint to our creator. It is sound for us to do so + /// with some `'r+` known to our creator, where `'r+: 'r`. + /// + /// The tricky bit here: this region `'r` may contain (a) any + /// number of points in the CFG and (b) any number of `end('x)` + /// elements of universally quantified regions. To communicate with + /// our creator, however, we have to pick exactly one universally + /// quantified region -- in other words, exactly one `end('x)` + /// element -- that they understand and which will be `'r+`. + /// + /// We do this as follows: + /// + /// - Ignore the CFG points in `'r`. All universally quantified regions + /// include the CFG anyhow. + /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding + /// a result `'y`. + /// - Finally, we take the non-local upper bound of `'y`. + fn promoted_type_test_bound(&self, lower_bound: RegionVid) -> RegionVid { + let inferred_values = self.inferred_values.as_ref().unwrap(); + + debug!( + "promoted_type_test_bound(lower_bound={:?}={})", + lower_bound, + inferred_values.region_value_str(lower_bound) + ); + + // Find the smallest universal region that contains all other + // universal regions within `region`. + let mut lub = self.universal_regions.fr_fn_body; + for ur in inferred_values.universal_regions_outlived_by(lower_bound) { + lub = self.universal_regions.postdom_upper_bound(lub, ur); + } + + debug!("promoted_type_test_bound: lub={:?}", lub); + + // Grow further to get smallest universal region known to + // creator. + let non_local_lub = self.universal_regions.non_local_upper_bound(lub); + + debug!( + "promoted_type_test_bound: non_local_lub={:?}", + non_local_lub + ); + + non_local_lub + } + /// Test if `test` is true when applied to `lower_bound` at /// `point`, and returns true or false. fn eval_region_test( @@ -487,13 +593,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { .iter() .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), - RegionTest::Any(tests) => tests.iter().any(|test| { - self.eval_region_test(mir, point, lower_bound, test) - }), + RegionTest::Any(tests) => tests + .iter() + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), - RegionTest::All(tests) => tests.iter().all(|test| { - self.eval_region_test(mir, point, lower_bound, test) - }), + RegionTest::All(tests) => tests + .iter() + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), } } @@ -772,17 +878,18 @@ impl fmt::Debug for Constraint { } } -pub trait ClosureRegionRequirementsExt { +pub trait ClosureRegionRequirementsExt<'gcx> { fn apply_requirements<'tcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, ); } -impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { +impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx> { /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of @@ -797,7 +904,8 @@ impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { /// requirements. fn apply_requirements<'tcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, @@ -843,8 +951,15 @@ impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> { infcx.sub_regions(origin, outlived_region, region); } - ClosureOutlivesSubject::Ty(_ty) => { - bug!("TODO not yet implemented -- closure outlives subject of a type"); + ClosureOutlivesSubject::Ty(ty) => { + infcx.register_region_obligation( + body_id, + RegionObligation { + sup_type: ty, + sub_region: outlived_region, + cause: ObligationCause::misc(outlives_requirement.blame_span, body_id), + }, + ); } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index f7493f653e0..5c3cdbe2207 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1409,6 +1409,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) { closure_region_requirements.apply_requirements( self.infcx, + self.body_id, location, *def_id, *substs, diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index ce931a68a0c..d3e75626e63 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -267,6 +267,20 @@ impl<'tcx> UniversalRegions<'tcx> { self.num_universals } + /// Given two universal regions, returns the postdominating + /// upper-bound (effectively the least upper bound). + /// + /// (See `TransitiveRelation::postdom_upper_bound` for details on + /// the postdominating upper bound in general.) + pub fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid { + assert!(self.is_universal_region(fr1)); + assert!(self.is_universal_region(fr2)); + *self.relations + .inverse_outlives + .postdom_upper_bound(&fr1, &fr2) + .unwrap_or(&self.fr_static) + } + /// Finds an "upper bound" for `fr` that is not local. In other /// words, returns the smallest (*) known region `fr1` that (a) /// outlives `fr` and (b) is not local. This cannot fail, because @@ -431,7 +445,10 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // - `'r: 'fn_body` for every (other) universally quantified // region `'r`, all of which are provided by our caller for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) { - debug!("build: relating free region {:?} to itself and to 'static", fr); + debug!( + "build: relating free region {:?} to itself and to 'static", + fr + ); self.relations.relate_universal_regions(fr, fr); self.relations.relate_universal_regions(fr_static, fr); self.relations.relate_universal_regions(fr, fr_fn_body); @@ -442,15 +459,21 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // we should not have created any more variables assert_eq!(self.infcx.num_region_vars(), num_universals); - debug!("build: global regions = {}..{}", - FIRST_GLOBAL_INDEX, - first_extern_index); - debug!("build: extern regions = {}..{}", - first_extern_index, - first_local_index); - debug!("build: local regions = {}..{}", - first_local_index, - num_universals); + debug!( + "build: global regions = {}..{}", + FIRST_GLOBAL_INDEX, + first_extern_index + ); + debug!( + "build: extern regions = {}..{}", + first_extern_index, + first_local_index + ); + debug!( + "build: local regions = {}..{}", + first_local_index, + num_universals + ); UniversalRegions { indices, diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs new file mode 100644 index 00000000000..6b23c82c771 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -0,0 +1,66 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::fmt::Debug; + +fn with_signature<'a, T, F>(x: Box, op: F) -> Box + where F: FnOnce(Box) -> Box +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box) -> Box +where + T: Debug, +{ + // Here, the closure winds up being required to prove that `T: + // 'a`. In principle, it could know that, except that it is + // type-checked in a fully generic way, and hence it winds up with + // a propagated requirement that `T: '_#2`, where `'_#2` appears + // in the return type. The caller makes the mapping from `'_#2` to + // `'a` (and subsequently reports an error). + + with_signature(x, |y| y) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr new file mode 100644 index 00000000000..721896a93ff --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -0,0 +1,58 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:27 + | +37 | with_signature(x, |y| y) + | ^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 + | +53 | x + | ^ + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 + | +37 | with_signature(x, |y| y) + | ^^^^^ + | + = note: defining type: DefId(0/1:14 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#4r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-return-type.rs:37:23: 37:28, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 + | +37 | with_signature(x, |y| y) + | ^^^^^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:1 + | +26 | / fn no_region<'a, T>(x: Box) -> Box +27 | | where +28 | | T: Debug, +29 | | { +... | +39 | | //~| ERROR failed type test +40 | | } + | |_^ + | + = note: defining type: DefId(0/0:5 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-closure-outlives-from-return-type.rs:53:5: 53:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } + --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 + | +53 | x + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs new file mode 100644 index 00000000000..54f7b4fa50d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -0,0 +1,96 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we can propagate `T: 'a` obligations to our caller. See +// `correct_region` for an explanation of how this test is setup; it's +// somewhat intricate. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn with_signature<'a, T, F>(a: Cell<&'a ()>, b: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(a, b) +} + +fn require<'a, T>(_a: &Cell<&'a ()>, _b: &T) +where + T: 'a, +{ +} + +#[rustc_regions] +fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { + with_signature(a, b, |x, y| { + //~^ ERROR failed type test + // + // See `correct_region`, which explains the point of this + // test. The only difference is that, in the case of this + // function, there is no where clause *anywhere*, and hence we + // get an error (but reported by the closure creator). + require(&x, &y) + //~^ WARNING not reporting region error due to -Znll + }) +} + +#[rustc_regions] +fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +where + T: 'a, +{ + with_signature(a, b, |x, y| { + // Key point of this test: + // + // The *closure* is being type-checked with all of its free + // regions "universalized". In particular, it does not know + // that `x` has the type `Cell<&'a ()>`, but rather treats it + // as if the type of `x` is `Cell<&'A ()>`, where `'A` is some + // fresh, independent region distinct from the `'a` which + // appears in the environment. The call to `require` here + // forces us then to prove that `T: 'A`, but the closure + // cannot do it on its own. It has to surface this requirement + // to its creator (which knows that `'a == 'A`). + require(&x, &y) + }) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, +{ + with_signature(a, b, |x, y| { + //~^ ERROR failed type test + // See `correct_region` + require(&x, &y) + //~^ WARNING not reporting region error due to -Znll + }) +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, + 'b: 'a, +{ + with_signature(a, b, |x, y| { + // See `correct_region` + require(&x, &y) + }) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr new file mode 100644 index 00000000000..748333badce --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -0,0 +1,191 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:45:9 + | +45 | require(&x, &y) + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:79:9 + | +79 | require(&x, &y) + | ^^^^^^^ + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 + | +38 | with_signature(a, b, |x, y| { + | __________________________^ +39 | | //~^ ERROR failed type test +40 | | // +41 | | // See `correct_region`, which explains the point of this +... | +46 | | //~^ WARNING not reporting region error due to -Znll +47 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:16 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:55:26 + | +55 | with_signature(a, b, |x, y| { + | __________________________^ +56 | | // Key point of this test: +57 | | // +58 | | // The *closure* is being type-checked with all of its free +... | +67 | | require(&x, &y) +68 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:19 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 + | +76 | with_signature(a, b, |x, y| { + | __________________________^ +77 | | //~^ ERROR failed type test +78 | | // See `correct_region` +79 | | require(&x, &y) +80 | | //~^ WARNING not reporting region error due to -Znll +81 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:23 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:90:26 + | +90 | with_signature(a, b, |x, y| { + | __________________________^ +91 | | // See `correct_region` +92 | | require(&x, &y) +93 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:27 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + +error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#3r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26: 47:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 + | +38 | with_signature(a, b, |x, y| { + | __________________________^ +39 | | //~^ ERROR failed type test +40 | | // +41 | | // See `correct_region`, which explains the point of this +... | +46 | | //~^ WARNING not reporting region error due to -Znll +47 | | }) + | |_____^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:37:1 + | +37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { +38 | | with_signature(a, b, |x, y| { +39 | | //~^ ERROR failed type test +40 | | // +... | +47 | | }) +48 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]) with substs [ + T + ] + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:51:1 + | +51 | / fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +52 | | where +53 | | T: 'a, +54 | | { +... | +68 | | }) +69 | | } + | |_^ + | + = note: defining type: DefId(0/0:7 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26: 81:6, test: IsOutlivedByAnyRegionIn(['_#1r, '_#3r]) } + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 + | +76 | with_signature(a, b, |x, y| { + | __________________________^ +77 | | //~^ ERROR failed type test +78 | | // See `correct_region` +79 | | require(&x, &y) +80 | | //~^ WARNING not reporting region error due to -Znll +81 | | }) + | |_____^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1 + | +72 | / fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +73 | | where +74 | | T: 'b, +75 | | { +... | +81 | | }) +82 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:85:1 + | +85 | / fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +86 | | where +87 | | T: 'b, +88 | | 'b: 'a, +... | +93 | | }) +94 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 3a5842afe1e10023d84b7c1d44155c356f8ae4e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Dec 2017 05:18:58 -0500 Subject: add a new RegionKind variant: ReClosureBound This is needed to allow the `ClosureRegionRequirements` to capture types that include regions. --- src/librustc/ich/impls_ty.rs | 3 +++ src/librustc/infer/combine.rs | 8 ++++++++ src/librustc/infer/error_reporting/mod.rs | 8 ++++++++ src/librustc/infer/freshen.rs | 7 +++++++ src/librustc/infer/lexical_region_resolve/mod.rs | 7 ++++++- src/librustc/mir/mod.rs | 9 +++++++++ src/librustc/ty/sty.rs | 9 +++++++++ src/librustc/ty/util.rs | 2 ++ src/librustc/util/ppaux.rs | 8 ++++++++ src/librustc_borrowck/borrowck/gather_loans/mod.rs | 1 + src/librustc_mir/borrow_check/error_reporting.rs | 1 + src/librustc_typeck/variance/constraints.rs | 1 + src/librustdoc/clean/mod.rs | 1 + 13 files changed, 64 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 2655e2acbbd..ea3a1074aa2 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -75,6 +75,9 @@ for ty::RegionKind { ty::ReFree(ref free_region) => { free_region.hash_stable(hcx, hasher); } + ty::ReClosureBound(vid) => { + vid.hash_stable(hcx, hasher); + } ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) => { diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 50a37e12531..f7bc092a3d7 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -475,6 +475,14 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' ty::Bivariant | ty::Covariant | ty::Contravariant => (), } } + + ty::ReClosureBound(..) => { + span_bug!( + self.span, + "encountered unexpected ReClosureBound: {:?}", + r, + ); + } } // FIXME: This is non-ideal because we don't give a diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 514b29120a9..3e3aea0256e 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -240,6 +240,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::ReErased => { (format!("lifetime {:?}", region), None) } + + // We shouldn't encounter an error message with ReClosureBound. + ty::ReClosureBound(..) => { + bug!( + "encountered unexpected ReClosureBound: {:?}", + region, + ); + } }; let message = format!("{}{}{}", prefix, description, suffix); if let Some(span) = span { diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 426c61e9ac0..1783d5abfc7 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -113,6 +113,13 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { // replace all free regions with 'erased self.tcx().types.re_erased } + + ty::ReClosureBound(..) => { + bug!( + "encountered unexpected ReClosureBound: {:?}", + r, + ); + } } } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 5a4f2157298..3ac4ec5bee4 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -258,7 +258,12 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.region_rels.tcx; match (a, b) { - (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + (&ty::ReClosureBound(..), _) | + (_, &ty::ReClosureBound(..)) | + (&ReLateBound(..), _) | + (_, &ReLateBound(..)) | + (&ReErased, _) | + (_, &ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d7afce7de46..dd3dd1e06de 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1831,6 +1831,15 @@ pub struct GeneratorLayout<'tcx> { /// instance of the closure is created, the corresponding free regions /// can be extracted from its type and constrained to have the given /// outlives relationship. +/// +/// In some cases, we have to record outlives requirements between +/// types and regions as well. In that case, if those types include +/// any regions, those regions are recorded as `ReClosureBound` +/// instances assigned one of these same indices. Those regions will +/// be substituted away by the creator. We use `ReClosureBound` in +/// that case because the regions must be allocated in the global +/// TyCtxt, and hence we cannot use `ReVar` (which is what we use +/// internally within the rest of the NLL code). #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct ClosureRegionRequirements<'gcx> { /// The number of external regions defined on the closure. In our diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 2f72b3dde42..02729c6d600 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1036,6 +1036,12 @@ pub enum RegionKind { /// Erased region, used by trait selection, in MIR and during trans. ReErased, + + /// These are regions bound in the "defining type" for a + /// closure. They are used ONLY as part of the + /// `ClosureRegionRequirements` that are produced by MIR borrowck. + /// See `ClosureRegionRequirements` for more details. + ReClosureBound(RegionVid), } impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {} @@ -1207,6 +1213,9 @@ impl RegionKind { } ty::ReErased => { } + ty::ReClosureBound(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } } match *self { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2e9e45c9ffe..84d5f547f1b 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -822,6 +822,8 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => { self.def_id(def_id); } + + ty::ReClosureBound(..) | ty::ReLateBound(..) | ty::ReFree(..) | ty::ReScope(..) | diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 9ff3d73f5c4..5bfa6464568 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -733,6 +733,9 @@ define_print! { ty::ReErased => Ok(()), ty::ReStatic => write!(f, "'static"), ty::ReEmpty => write!(f, "'"), + + // The user should never encounter these in unsubstituted form. + ty::ReClosureBound(vid) => write!(f, "{:?}", vid), } } debug { @@ -743,6 +746,11 @@ define_print! { data.name) } + ty::ReClosureBound(ref vid) => { + write!(f, "ReClosureBound({:?})", + vid) + } + ty::ReLateBound(binder_id, ref bound_region) => { write!(f, "ReLateBound({:?}, {:?})", binder_id, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 8654f2a50e4..5cbe2822e5c 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -367,6 +367,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { ty::ReStatic => self.item_ub, ty::ReEmpty | + ty::ReClosureBound(..) | ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) | diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 31a94499fd0..2c0fa9878aa 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -381,6 +381,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { }, (RegionKind::ReLateBound(_, _), _) | (RegionKind::ReSkolemized(_, _), _) | + (RegionKind::ReClosureBound(_), _) | (RegionKind::ReErased, _) => { span_bug!(drop_span, "region does not make sense in this context"); }, diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index ef6552c8e33..df42d5eaa0a 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -465,6 +465,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::ReFree(..) | + ty::ReClosureBound(..) | ty::ReScope(..) | ty::ReVar(..) | ty::ReSkolemized(..) | diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 91908de98a6..c7657c9b2ff 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1055,6 +1055,7 @@ impl Clean> for ty::RegionKind { ty::ReVar(..) | ty::ReSkolemized(..) | ty::ReEmpty | + ty::ReClosureBound(_) | ty::ReErased => None } } -- cgit 1.4.1-3-g733a5 From 3fcb13ae458ce6e2eedd34461b4e43c7be31b6b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Dec 2017 13:15:23 -0500 Subject: handle projections with regions --- .../borrow_check/nll/region_infer/mod.rs | 279 ++++++++++++------ .../borrow_check/nll/universal_regions.rs | 17 +- src/test/ui/nll/ty-outlives/projection-fn.rs | 53 ---- src/test/ui/nll/ty-outlives/projection-fn.stderr | 26 -- .../ty-outlives/projection-no-regions-closure.rs | 68 +++++ .../projection-no-regions-closure.stderr | 157 ++++++++++ .../ui/nll/ty-outlives/projection-no-regions-fn.rs | 53 ++++ .../ty-outlives/projection-no-regions-fn.stderr | 26 ++ .../ty-outlives/projection-one-region-closure.rs | 106 +++++++ .../projection-one-region-closure.stderr | 194 ++++++++++++ .../projection-one-region-trait-bound-closure.rs | 106 +++++++ ...rojection-one-region-trait-bound-closure.stderr | 204 +++++++++++++ ...ection-one-region-trait-bound-static-closure.rs | 99 +++++++ ...on-one-region-trait-bound-static-closure.stderr | 155 ++++++++++ .../projection-two-region-trait-bound-closure.rs | 135 +++++++++ ...rojection-two-region-trait-bound-closure.stderr | 326 +++++++++++++++++++++ 16 files changed, 1830 insertions(+), 174 deletions(-) delete mode 100644 src/test/ui/nll/ty-outlives/projection-fn.rs delete mode 100644 src/test/ui/nll/ty-outlives/projection-fn.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr (limited to 'src') 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 244abb40798..f03e8bd7ac1 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -19,7 +19,7 @@ use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Location, Mir}; use rustc::traits::ObligationCause; -use rustc::ty::{self, RegionVid, TypeFoldable}; +use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt; use std::rc::Rc; @@ -478,7 +478,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements: &mut Vec>, ) -> bool { let tcx = infcx.tcx; - let gcx = tcx.global_tcx(); let TypeTest { generic_kind, @@ -488,80 +487,158 @@ impl<'tcx> RegionInferenceContext<'tcx> { test: _, } = type_test; - // TODO. For now, just fail to promote anything with a - // region. This is obviously too strict: we will for example - // fail to promote `>::Bar` to our - // caller. But it is always sound not to promote, that just - // means more errors, and ignoring regions is a convenient - // starting point. This is because we would want to promote to - // a type that references the region-vids of the closure, for - // which we have no global representation just now. let generic_ty = generic_kind.to_ty(tcx); - if generic_ty.has_free_regions() { - return false; - } - let generic_ty = gcx.lift(&generic_ty).unwrap(); + let subject = match self.try_promote_type_test_subject(infcx, generic_ty) { + Some(s) => s, + None => return false, + }; // Find some bounding subject-region R+ that is a super-region // of the existing subject-region R. This should be a non-local, universal // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`. - let lower_bound_plus = self.promoted_type_test_bound(*lower_bound); + let lower_bound_plus = self.non_local_universal_upper_bound(*lower_bound); assert!(self.universal_regions.is_universal_region(lower_bound_plus)); assert!(!self.universal_regions .is_local_free_region(lower_bound_plus)); propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Ty(generic_ty), + subject, outlived_free_region: lower_bound_plus, blame_span: *span, }); true } - /// Here, `lower_bound` (henceforth, `'r`) represents the bound from - /// some type-test `T: 'r`. We are a closure and have found that - /// `T: 'r` is not locally satisfiable, so we want to propagate - /// this constraint to our creator. It is sound for us to do so - /// with some `'r+` known to our creator, where `'r+: 'r`. + /// When we promote a type test `T: 'r`, we have to convert the + /// type `T` into something we can store in a query result (so + /// something allocated for `'gcx`). This is problematic if `ty` + /// contains regions. During the course of NLL region checking, we + /// will have replaced all of those regions with fresh inference + /// variables. To create a test subject, we want to replace those + /// inference variables with some region from the closure + /// signature -- this is not always possible, so this is a + /// fallible process. Presuming we do find a suitable region, we + /// will represent it with a `ReClosureBound`, which is a + /// `RegionKind` variant that can be allocated in the gcx. + fn try_promote_type_test_subject<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + let tcx = infcx.tcx; + let gcx = tcx.global_tcx(); + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + + debug!("try_promote_type_test_subject(ty = {:?})", ty); + + let ty = tcx.fold_regions(&ty, &mut false, |r, _depth| { + let region_vid = self.to_region_vid(r); + + // The challenge if this. We have some region variable `r` + // whose value is a set of CFG points and universal + // regions. We want to find if that set is *equivalent* to + // any of the named regions found in the closure. + // + // To do so, we compute the + // `non_local_universal_upper_bound`. This will be a + // non-local, universal region that is greater than `r`. + // However, it might not be *contained* within `r`, so + // then we further check whether this bound is contained + // in `r`. If so, we can say that `r` is equivalent to the + // bound. + // + // Let's work through a few examples. For these, imagine + // that we have 3 non-local regions (I'll denote them as + // `'static`, `'a`, and `'b`, though of course in the code + // they would be represented with indices) where: + // + // - `'static: 'a` + // - `'static: 'b` + // + // First, let's assume that `r` is some existential + // variable with an inferred value `{'a, 'static}` (plus + // some CFG nodes). In this case, the non-local upper + // bound is `'static`, since that outlives `'a`. `'static` + // is also a member of `r` and hence we consider `r` + // equivalent to `'static` (and replace it with + // `'static`). + // + // Now let's consider the inferred value `{'a, 'b}`. This + // means `r` is effectively `'a | 'b`. I'm not sure if + // this can come about, actually, but assuming it did, we + // would get a non-local upper bound of `'static`. Since + // `'static` is not contained in `r`, we would fail to + // find an equivalent. + let upper_bound = self.non_local_universal_upper_bound(region_vid); + if inferred_values.contains(region_vid, upper_bound) { + tcx.mk_region(ty::ReClosureBound(upper_bound)) + } else { + // In the case of a failure, use a `ReVar` + // result. This will cause the `lift` later on to + // fail. + r + } + }); + debug!("try_promote_type_test_subject: folded ty = {:?}", ty); + + // `lift` will only fail if we failed to promote some region. + let ty = gcx.lift(&ty)?; + + Some(ClosureOutlivesSubject::Ty(ty)) + } + + /// Given some universal or existential region `r`, finds a + /// non-local, universal region `r+` that outlives `r` at entry to (and + /// exit from) the closure. In the worst case, this will be + /// `'static`. /// - /// The tricky bit here: this region `'r` may contain (a) any - /// number of points in the CFG and (b) any number of `end('x)` - /// elements of universally quantified regions. To communicate with - /// our creator, however, we have to pick exactly one universally - /// quantified region -- in other words, exactly one `end('x)` - /// element -- that they understand and which will be `'r+`. + /// This is used for two purposes. First, if we are propagated + /// some requirement `T: r`, we can use this method to enlarge `r` + /// to something we can encode for our creator (which only knows + /// about non-local, universal regions). It is also used when + /// encoding `T` as part of `try_promote_type_test_subject` (see + /// that fn for details). /// - /// We do this as follows: + /// Since `r` is (potentially) an existential region, it has some + /// value which may include (a) any number of points in the CFG + /// and (b) any number of `end('x)` elements of universally + /// quantified regions. To convert this into a single universal + /// region we do as follows: /// /// - Ignore the CFG points in `'r`. All universally quantified regions /// include the CFG anyhow. /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. /// - Finally, we take the non-local upper bound of `'y`. - fn promoted_type_test_bound(&self, lower_bound: RegionVid) -> RegionVid { + /// - This uses `UniversalRegions::non_local_upper_bound`, which + /// is similar to this method but only works on universal + /// regions). + fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { let inferred_values = self.inferred_values.as_ref().unwrap(); debug!( - "promoted_type_test_bound(lower_bound={:?}={})", - lower_bound, - inferred_values.region_value_str(lower_bound) + "non_local_universal_upper_bound(r={:?}={})", + r, + inferred_values.region_value_str(r) ); // Find the smallest universal region that contains all other // universal regions within `region`. let mut lub = self.universal_regions.fr_fn_body; - for ur in inferred_values.universal_regions_outlived_by(lower_bound) { + for ur in inferred_values.universal_regions_outlived_by(r) { lub = self.universal_regions.postdom_upper_bound(lub, ur); } - debug!("promoted_type_test_bound: lub={:?}", lub); + debug!("non_local_universal_upper_bound: lub={:?}", lub); // Grow further to get smallest universal region known to // creator. let non_local_lub = self.universal_regions.non_local_upper_bound(lub); debug!( - "promoted_type_test_bound: non_local_lub={:?}", + "non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub ); @@ -680,32 +757,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Go through each of the universal regions `fr` and check that // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. - let mut outlives_requirements = vec![]; for (fr, _) in universal_definitions { - self.check_universal_region(infcx, fr, &mut outlives_requirements); - - // Propagate unsatisfied requirements if possible, else - // report them. - if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { - propagated_outlives_requirements.extend(outlives_requirements.drain(..)); - } else { - for outlives_requirement in outlives_requirements.drain(..) { - let fr = match outlives_requirement.subject { - ClosureOutlivesSubject::Region(fr) => fr, - _ => span_bug!( - outlives_requirement.blame_span, - "check_universal_region() produced requirement w/ non-region subject" - ), - }; - - self.report_error( - infcx, - fr, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); - } - } + self.check_universal_region(infcx, fr, &mut propagated_outlives_requirements); } } @@ -721,7 +774,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, longer_fr: RegionVid, - propagated_outlives_requirements: &mut Vec>, + propagated_outlives_requirements: &mut Option<&mut Vec>>, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -743,33 +796,39 @@ impl<'tcx> RegionInferenceContext<'tcx> { let blame_span = self.blame_span(longer_fr, shorter_fr); - // Shrink `fr` until we find a non-local region (if we do). - // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. - if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) { - debug!("check_universal_region: fr_minus={:?}", fr_minus); - - // Grow `shorter_fr` until we find a non-local - // regon. (We always will.) We'll call that - // `shorter_fr+` -- it's ever so slightly larger than - // `fr`. - let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr); - debug!( - "check_universal_region: shorter_fr_plus={:?}", - shorter_fr_plus - ); + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // Shrink `fr` until we find a non-local region (if we do). + // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. + if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) { + debug!("check_universal_region: fr_minus={:?}", fr_minus); + + // Grow `shorter_fr` until we find a non-local + // regon. (We always will.) We'll call that + // `shorter_fr+` -- it's ever so slightly larger than + // `fr`. + let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr); + debug!( + "check_universal_region: shorter_fr_plus={:?}", + shorter_fr_plus + ); - // Push the constraint `fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: shorter_fr_plus, - blame_span: blame_span, - }); - return; + // Push the constraint `fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: shorter_fr_plus, + blame_span: blame_span, + }); + return; + } } - // If we could not shrink `fr` to something smaller that - // the external users care about, then we can't pass the - // buck; just report an error. + // If we are not in a context where we can propagate + // errors, or we could not shrink `fr` to something + // smaller, then just report an error. + // + // Note: in this case, we use the unapproximated regions + // to report the error. This gives better error messages + // in some cases. self.report_error(infcx, longer_fr, shorter_fr, blame_span); } } @@ -878,8 +937,8 @@ impl fmt::Debug for Constraint { } } -pub trait ClosureRegionRequirementsExt<'gcx> { - fn apply_requirements<'tcx>( +pub trait ClosureRegionRequirementsExt<'gcx, 'tcx> { + fn apply_requirements( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, @@ -887,9 +946,18 @@ pub trait ClosureRegionRequirementsExt<'gcx> { closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, ); + + fn subst_closure_mapping( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + closure_mapping: &IndexVec>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>; } -impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx> { +impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequirements<'gcx> { /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of @@ -902,7 +970,7 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx /// a vector. Then we can just index into that vector to extract /// out the corresponding region from T and apply the /// requirements. - fn apply_requirements<'tcx>( + fn apply_requirements( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, @@ -927,7 +995,7 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx // into a vector. These are the regions that we will be // relating to one another. let closure_mapping = - UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids); + &UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids); debug!("apply_requirements: closure_mapping={:?}", closure_mapping); // Create the predicates. @@ -943,15 +1011,24 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx debug!( "apply_requirements: region={:?} \ outlived_region={:?} \ - outlives_requirements={:?}", + outlives_requirement={:?}", region, outlived_region, - outlives_requirement + outlives_requirement, ); infcx.sub_regions(origin, outlived_region, region); } ClosureOutlivesSubject::Ty(ty) => { + let ty = self.subst_closure_mapping(infcx, closure_mapping, &ty); + debug!( + "apply_requirements: ty={:?} \ + outlived_region={:?} \ + outlives_requirement={:?}", + ty, + outlived_region, + outlives_requirement, + ); infcx.register_region_obligation( body_id, RegionObligation { @@ -964,4 +1041,22 @@ impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx } } } + + fn subst_closure_mapping( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + closure_mapping: &IndexVec>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx> + { + infcx.tcx.fold_regions(value, &mut false, |r, _depth| { + if let ty::ReClosureBound(vid) = r { + closure_mapping[*vid] + } else { + bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) + } + }) + } } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index d3e75626e63..5f4a72542b2 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -247,17 +247,24 @@ impl<'tcx> UniversalRegions<'tcx> { (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new) } - /// True if `r` is classied as a global region. + /// True if `r` is classified as a global region. pub fn is_global_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Global) } - /// True if `r` is classied as an external region. + /// True if `r` is classified as an external region. pub fn is_extern_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::External) } - /// True if `r` is classied as an local region. + /// True if `r` is a free region that is classified as global or + /// extern. This is an important category, because these regions + /// can be referenced in `ClosureRegionRequirements`. + pub fn is_non_local_free_region(&self, r: RegionVid) -> bool { + self.region_classification(r) == Some(RegionClassification::Local) + } + + /// True if `r` is classified as an local region. pub fn is_local_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Local) } @@ -324,6 +331,10 @@ impl<'tcx> UniversalRegions<'tcx> { relation: &TransitiveRelation, fr0: RegionVid, ) -> Option { + // This method assumes that `fr0` is one of the universally + // quantified region variables. + assert!(self.is_universal_region(fr0)); + let mut external_parents = vec![]; let mut queue = vec![&fr0]; diff --git a/src/test/ui/nll/ty-outlives/projection-fn.rs b/src/test/ui/nll/ty-outlives/projection-fn.rs deleted file mode 100644 index 677d1352788..00000000000 --- a/src/test/ui/nll/ty-outlives/projection-fn.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags:-Znll -Zborrowck=mir - -#![allow(warnings)] -#![feature(dyn_trait)] - -trait Anything { } - -impl Anything for T { } - -fn no_region<'a, T>(mut x: T) -> Box -where - T: Iterator, -{ - Box::new(x.next()) - //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test -} - -fn correct_region<'a, T>(mut x: T) -> Box -where - T: 'a + Iterator, -{ - Box::new(x.next()) -} - -fn wrong_region<'a, 'b, T>(mut x: T) -> Box -where - T: 'b + Iterator, -{ - Box::new(x.next()) - //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test -} - -fn outlives_region<'a, 'b, T>(mut x: T) -> Box -where - T: 'b + Iterator, - 'b: 'a, -{ - Box::new(x.next()) -} - -fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr deleted file mode 100644 index 77eabef6545..00000000000 --- a/src/test/ui/nll/ty-outlives/projection-fn.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: not reporting region error due to -Znll - --> $DIR/projection-fn.rs:24:5 - | -24 | Box::new(x.next()) - | ^^^^^^^^^^^^^^^^^^ - -warning: not reporting region error due to -Znll - --> $DIR/projection-fn.rs:40:5 - | -40 | Box::new(x.next()) - | ^^^^^^^^^^^^^^^^^^ - -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } - --> $DIR/projection-fn.rs:24:5 - | -24 | Box::new(x.next()) - | ^^^^^^^^^^^^^^^^^^ - -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } - --> $DIR/projection-fn.rs:40:5 - | -40 | Box::new(x.next()) - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs new file mode 100644 index 00000000000..b91c01fb671 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -0,0 +1,68 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +// Tests closures that propagate an outlives relationship to their +// creator where the subject is a projection with no regions (`::Item`, to be exact). + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +trait Anything { } + +impl Anything for T { } + +fn with_signature<'a, T, F>(x: Box, op: F) -> Box + where F: FnOnce(Box) -> Box +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box) -> Box +where + T: Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr new file mode 100644 index 00000000000..1d124f2d49a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -0,0 +1,157 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-closure.rs:36:31 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-closure.rs:54:31 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^ + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:36:23 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:15 ~ projection_no_regions_closure[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where ::Item: '_#2r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:46:23 + | +46 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:18 ~ projection_no_regions_closure[317d]::correct_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where ::Item: '_#2r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:54:23 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:22 ~ projection_no_regions_closure[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 4 + = note: where ::Item: '_#3r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:65:23 + | +65 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:26 ~ projection_no_regions_closure[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 4 + = note: where ::Item: '_#3r + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:36:23: 36:49, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-no-regions-closure.rs:36:23 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:32:1 + | +32 | / fn no_region<'a, T>(x: Box) -> Box +33 | | where +34 | | T: Iterator, +35 | | { +... | +38 | | //~| ERROR failed type test +39 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ projection_no_regions_closure[317d]::no_region[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:42:1 + | +42 | / fn correct_region<'a, T>(x: Box) -> Box +43 | | where +44 | | T: 'a + Iterator, +45 | | { +46 | | with_signature(x, |mut y| Box::new(y.next())) +47 | | } + | |_^ + | + = note: defining type: DefId(0/0:7 ~ projection_no_regions_closure[317d]::correct_region[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:54:23: 54:49, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } + --> $DIR/projection-no-regions-closure.rs:54:23 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:50:1 + | +50 | / fn wrong_region<'a, 'b, T>(x: Box) -> Box +51 | | where +52 | | T: 'b + Iterator, +53 | | { +... | +56 | | //~| ERROR failed type test +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_no_regions_closure[317d]::wrong_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:60:1 + | +60 | / fn outlives_region<'a, 'b, T>(x: Box) -> Box +61 | | where +62 | | T: 'b + Iterator, +63 | | 'b: 'a, +64 | | { +65 | | with_signature(x, |mut y| Box::new(y.next())) +66 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_no_regions_closure[317d]::outlives_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs new file mode 100644 index 00000000000..b7822eb259b --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -0,0 +1,53 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] + +trait Anything { } + +impl Anything for T { } + +fn no_region<'a, T>(mut x: T) -> Box +where + T: Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(mut x: T) -> Box +where + T: 'a + Iterator, +{ + Box::new(x.next()) +} + +fn wrong_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + Box::new(x.next()) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr new file mode 100644 index 00000000000..5c3bd04f3b1 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-no-regions-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } + --> $DIR/projection-no-regions-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs new file mode 100644 index 00000000000..cd9b1c2a8ce --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -0,0 +1,106 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test cases where we constrain `>::AssocType` to +// outlive `'a` and there are no bounds in the trait definition of +// `Anything`. This means that the constraint can only be satisfied in two +// ways: +// +// - by ensuring that `T: 'a` and `'b: 'a`, or +// - by something in the where clauses. +// +// As of this writing, the where clause option does not work because +// of limitations in our region inferencing system (this is true both +// with and without NLL). See `projection_outlives`. +// +// Ensuring that both `T: 'a` and `'b: 'a` holds does work (`elements_outlive`). + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T: 'a, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr new file mode 100644 index 00000000000..d187a094ec6 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -0,0 +1,194 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:56:39 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:68:39 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:90:39 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-one-region-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + = note: where '_#1r: '_#2r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:68:29 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:90:29 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:103:29 + | +103 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:56:29: 56:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } + --> $DIR/projection-one-region-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-one-region-closure.rs:56:20 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:52:1 + | +52 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +53 | | where +54 | | T: Anything<'b>, +55 | | { +... | +59 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +60 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:68:29: 68:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } + --> $DIR/projection-one-region-closure.rs:68:29 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-closure.rs:68:20 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:63:1 + | +63 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +64 | | where +65 | | T: Anything<'b>, +66 | | 'a: 'a, +... | +71 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +72 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:90:29: 90:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } + --> $DIR/projection-one-region-closure.rs:90:29 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-closure.rs:90:20 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:75:1 + | +75 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +76 | | where +77 | | T: Anything<'b>, +78 | | T::AssocType: 'a, +... | +93 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +94 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:97:1 + | +97 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +98 | | where +99 | | T: Anything<'b>, +100 | | T: 'a, +... | +103 | | with_signature(cell, t, |cell, t| require(cell, t)); +104 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs new file mode 100644 index 00000000000..e179927dfb0 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs @@ -0,0 +1,106 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test cases where we constrain `>::AssocType` to +// outlive `'a` and there is a unique bound in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'b`. In this +// case, the best way to satisfy the trait bound is to show that `'b: +// 'a`, which can be done in various ways. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'a; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr new file mode 100644 index 00000000000..1088ae846fe --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -0,0 +1,204 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:48:39 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:59:39 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:80:39 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:48:29 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:59:29 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:80:29 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:91:29 + | +91 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:103:29 + | +103 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-one-region-trait-bound-closure.rs:48:20 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:44:1 + | +44 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +45 | | where +46 | | T: Anything<'b>, +47 | | { +... | +50 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +51 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-trait-bound-closure.rs:59:20 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:54:1 + | +54 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +55 | | where +56 | | T: Anything<'b>, +57 | | 'a: 'a, +... | +61 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +62 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-trait-bound-closure.rs:80:20 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:65:1 + | +65 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +66 | | where +67 | | T: Anything<'b>, +68 | | T::AssocType: 'a, +... | +82 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +83 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:86:1 + | +86 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +87 | | where +88 | | T: Anything<'b>, +89 | | 'b: 'a, +90 | | { +91 | | with_signature(cell, t, |cell, t| require(cell, t)); +92 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:95:1 + | +95 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +96 | | where +97 | | T: Anything<'a>, +98 | | { +... | +103 | | with_signature(cell, t, |cell, t| require(cell, t)); +104 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs new file mode 100644 index 00000000000..67e28af1146 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs @@ -0,0 +1,99 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test cases where we constrain `>::AssocType` to +// outlive `'static`. In this case, we don't get any errors, and in fact +// we don't even propagate constraints from the closures to the callers. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose +// must-compile-successfully + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'static; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr new file mode 100644 index 00000000000..986676d28d9 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr @@ -0,0 +1,155 @@ +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:47:29 + | +47 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:75:29 + | +75 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:84:29 + | +84 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:96:29 + | +96 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:43:1 + | +43 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +44 | | where +45 | | T: Anything<'b>, +46 | | { +47 | | with_signature(cell, t, |cell, t| require(cell, t)); +48 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:51:1 + | +51 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +52 | | where +53 | | T: Anything<'b>, +54 | | 'a: 'a, +55 | | { +56 | | with_signature(cell, t, |cell, t| require(cell, t)); +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:60:1 + | +60 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +61 | | where +62 | | T: Anything<'b>, +63 | | T::AssocType: 'a, +... | +75 | | with_signature(cell, t, |cell, t| require(cell, t)); +76 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:79:1 + | +79 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +80 | | where +81 | | T: Anything<'b>, +82 | | 'b: 'a, +83 | | { +84 | | with_signature(cell, t, |cell, t| require(cell, t)); +85 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:88:1 + | +88 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +89 | | where +90 | | T: Anything<'a>, +91 | | { +... | +96 | | with_signature(cell, t, |cell, t| require(cell, t)); +97 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs new file mode 100644 index 00000000000..f8f3065fff4 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -0,0 +1,135 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test cases where we constrain `>::AssocType` +// to outlive `'a` and there are two bounds in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'a` and +// `'b`. In this case, it's not clear what is the best way to satisfy +// the trait bound, and hence we propagate it to the caller as a type +// test. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a, 'b> { + type AssocType: 'a + 'b; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, 'c, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +#[rustc_regions] +fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'c: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a, 'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr new file mode 100644 index 00000000000..02650214129 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -0,0 +1,326 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:49:39 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:60:39 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:81:39 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:109:39 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:22 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where >::AssocType: '_#3r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:32 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:92:29 + | +92 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:37 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:101:29 + | +101 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:42 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:109:29 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:46 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where >::AssocType: '_#2r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:120:29 + | +120 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:50 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where >::AssocType: '_#3r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:132:29 + | +132 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:53 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where >::AssocType: '_#2r + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#5r, '_#6r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#7r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:49:29: 49:55, test: Any([IsOutlivedByAnyRegionIn(['_#6r, '_#5r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#5r, '_#6r])])]) } + --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:45:1 + | +45 | / fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +46 | | where +47 | | T: Anything<'b, 'c>, +48 | | { +... | +51 | | //~| ERROR failed type test +52 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:60:29: 60:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } + --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:55:1 + | +55 | / fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +56 | | where +57 | | T: Anything<'b, 'c>, +58 | | 'a: 'a, +... | +62 | | //~| ERROR failed type test +63 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:81:29: 81:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } + --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:66:1 + | +66 | / fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +67 | | where +68 | | T: Anything<'b, 'c>, +69 | | T::AssocType: 'a, +... | +83 | | //~| ERROR failed type test +84 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:87:1 + | +87 | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +88 | | where +89 | | T: Anything<'b, 'c>, +90 | | 'b: 'a, +91 | | { +92 | | with_signature(cell, t, |cell, t| require(cell, t)); +93 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:96:1 + | +96 | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +97 | | where +98 | | T: Anything<'b, 'c>, +99 | | 'c: 'a, +100 | | { +101 | | with_signature(cell, t, |cell, t| require(cell, t)); +102 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-two-region-trait-bound-closure.rs:109:20 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:105:1 + | +105 | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +106 | | where +107 | | T: Anything<'b, 'b>, +108 | | { +... | +111 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +112 | | } + | |_^ + | + = note: defining type: DefId(0/0:13 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:115:1 + | +115 | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +116 | | where +117 | | T: Anything<'b, 'b>, +118 | | 'b: 'a, +119 | | { +120 | | with_signature(cell, t, |cell, t| require(cell, t)); +121 | | } + | |_^ + | + = note: defining type: DefId(0/0:14 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:124:1 + | +124 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +125 | | where +126 | | T: Anything<'a, 'a>, +127 | | { +... | +132 | | with_signature(cell, t, |cell, t| require(cell, t)); +133 | | } + | |_^ + | + = note: defining type: DefId(0/0:15 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From e9824c50edd067fefb2e4fd6f6d1a4fe739a145b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 04:30:58 -0500 Subject: impose inputs/ouputs on MIR after the fact The input/output types found in `UniversalRegions` are not normalized. The old code used to assign them directly into the MIR, which would lead to errors when there was a projection in a argument or return type. This also led to some special cases in the `renumber` code. We now renumber uniformly but then pass the input/output types into the MIR type-checker, which equates them with the types found in MIR. This allows us to normalize at the same time. --- src/librustc_mir/borrow_check/nll/mod.rs | 4 +- src/librustc_mir/borrow_check/nll/renumber.rs | 68 ++-------------------- .../borrow_check/nll/type_check/mod.rs | 49 +++++++++++++++- .../borrow_check/nll/universal_regions.rs | 6 +- src/test/mir-opt/nll/named-lifetimes-basic.rs | 6 +- src/test/ui/nll/projection-return.rs | 29 +++++++++ 6 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 src/test/ui/nll/projection-return.rs (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 23fa2689053..6977d91d25a 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -53,7 +53,7 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( let universal_regions = UniversalRegions::new(infcx, def_id, param_env); // Replace all remaining regions with fresh inference variables. - renumber::renumber_mir(infcx, &universal_regions, mir); + renumber::renumber_mir(infcx, mir); let source = MirSource::item(def_id); mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(())); @@ -86,6 +86,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( param_env, mir, fr_fn_body, + universal_regions.input_tys, + universal_regions.output_ty, &liveness, flow_inits, move_data, diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 1262c238a13..79505405692 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -8,50 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc_data_structures::indexed_vec::Idx; use rustc::ty::subst::Substs; use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; -use rustc::mir::RETURN_PLACE; +use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; -use super::ToRegionVid; -use super::universal_regions::UniversalRegions; - /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. -pub fn renumber_mir<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - universal_regions: &UniversalRegions<'tcx>, - mir: &mut Mir<'tcx>, -) { +pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) { debug!("renumber_mir()"); debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); - // Update the return type and types of the arguments based on the - // `universal_regions` computation. - debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty); - mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty; - for (&input_ty, local) in universal_regions - .input_tys - .iter() - .zip((1..).map(Local::new)) - { - debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local); - mir.local_decls[local].ty = input_ty; - } - - let mut visitor = NLLVisitor { - infcx, - arg_count: mir.arg_count, - }; + let mut visitor = NLLVisitor { infcx }; visitor.visit_mir(mir); } struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - arg_count: usize, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { @@ -71,45 +45,13 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { self.infcx.next_nll_region_var(origin) }) } - - /// Checks that all the regions appearing in `value` have already - /// been renumbered. `FreeRegions` code should have done this. - fn assert_free_regions_are_renumbered(&self, value: &T) - where - T: TypeFoldable<'tcx>, - { - debug!("assert_free_regions_are_renumbered(value={:?})", value); - - self.infcx.tcx.for_each_free_region(value, |region| { - region.to_region_vid(); // will panic if `region` is not renumbered - }); - } - - fn is_argument_or_return_slot(&self, local: Local) -> bool { - // The first argument is return slot, next N are arguments. - local.index() <= self.arg_count - } } impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { - let is_arg = match ty_context { - TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local), - TyContext::ReturnTy(..) => true, - TyContext::Location(..) => false, - }; - debug!( - "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})", - ty, - is_arg, - ty_context - ); + debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context); - if is_arg { - self.assert_free_regions_are_renumbered(ty); - } else { - *ty = self.renumber_regions(ty_context, ty); - } + *ty = self.renumber_regions(ty_context, ty); debug!("visit_ty: ty={:?}", ty); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 5c3cdbe2207..6cdd77048c9 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -46,12 +46,31 @@ mod liveness; /// This phase of type-check ought to be infallible -- this is because /// the original, HIR-based type-check succeeded. So if any errors /// occur here, we will get a `bug!` reported. +/// +/// # Parameters +/// +/// - `infcx` -- inference context to use +/// - `body_id` -- body-id of the MIR being checked +/// - `param_env` -- parameter environment to use for trait solving +/// - `mir` -- MIR to type-check +/// - `implicit_region_bound` -- a region which all generic parameters are assumed +/// to outlive; should represent the fn body +/// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments; +/// the types of the input parameters found in the MIR itself will be equated with these +/// - `output_ty` -- fully liberaetd, but **not** normalized, expected return type; +/// the type for the RETURN_PLACE will be equated with this +/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness +/// constraints for the regions in the types of variables +/// - `flow_inits` -- results of a maybe-init dataflow analysis +/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis pub(crate) fn type_check<'gcx, 'tcx>( infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, implicit_region_bound: ty::Region<'tcx>, + input_tys: &[Ty<'tcx>], + output_ty: Ty<'tcx>, liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, @@ -62,7 +81,16 @@ pub(crate) fn type_check<'gcx, 'tcx>( param_env, mir, Some(implicit_region_bound), - &mut |cx| liveness::generate(cx, mir, liveness, flow_inits, move_data), + &mut |cx| { + liveness::generate(cx, mir, liveness, flow_inits, move_data); + + // Equate the input and output tys given by the user with + // the ones found in the MIR. + cx.equate_input_or_output(output_ty, mir.local_decls[RETURN_PLACE].ty); + for (&input_ty, local) in input_tys.iter().zip((1..).map(Local::new)) { + cx.equate_input_or_output(input_ty, mir.local_decls[local].ty); + } + }, ) } @@ -666,6 +694,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }) } + fn equate_input_or_output(&mut self, unnormalized_a: Ty<'tcx>, b: Ty<'tcx>) { + let start_position = Location { + block: START_BLOCK, + statement_index: 0, + }; + let a = self.normalize(&unnormalized_a, start_position); + if let Err(terr) = self.eq_types(a, b, start_position.at_self()) { + span_mirbug!( + self, + start_position, + "bad input or output {:?} normalized to {:?} should equal {:?} but got error {:?}", + unnormalized_a, + a, + b, + terr + ); + } + } + fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5f4a72542b2..99beae13bd9 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -69,12 +69,14 @@ pub struct UniversalRegions<'tcx> { /// closure type, but for a top-level function it's the `TyFnDef`. pub defining_ty: Ty<'tcx>, - /// The return type of this function, with all regions replaced - /// by their universal `RegionVid` equivalents. + /// The return type of this function, with all regions replaced by + /// their universal `RegionVid` equivalents. This type is **NOT + /// NORMALIZED**. pub output_ty: Ty<'tcx>, /// The fully liberated input types of this function, with all /// regions replaced by their universal `RegionVid` equivalents. + /// This type is **NOT NORMALIZED**. pub input_tys: &'tcx [Ty<'tcx>], /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 71304f71b61..f14979f9733 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -39,7 +39,11 @@ fn main() { // | '_#2r | {'_#2r, bb0[0], bb0[1]} // | '_#3r | {'_#3r, bb0[0], bb0[1]} // | '_#4r | {'_#4r, bb0[0], bb0[1]} +// | '_#5r | {'_#1r, bb0[0], bb0[1]} +// | '_#6r | {'_#2r, bb0[0], bb0[1]} +// | '_#7r | {'_#1r, bb0[0], bb0[1]} +// | '_#8r | {'_#3r, bb0[0], bb0[1]} // | // ... -// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { +// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool { // END rustc.use_x.nll.0.mir diff --git a/src/test/ui/nll/projection-return.rs b/src/test/ui/nll/projection-return.rs new file mode 100644 index 00000000000..31388cf50c5 --- /dev/null +++ b/src/test/ui/nll/projection-return.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir +// must-compile-successfully + +#![feature(rustc_attrs)] + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = u32; +} + +fn foo() -> <() as Foo>::Bar { + 22 +} + +fn main() { } + -- cgit 1.4.1-3-g733a5 From a118afe7ca742872f2064e26ddf97394d091ce3b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 04:56:03 -0500 Subject: add a test regarding relating closure and fn generics Turns out this works but we had no test targeting it. --- .../propagate-from-trait-match.rs | 59 +++++++++++++++++++++ .../propagate-from-trait-match.stderr | 60 ++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr (limited to 'src') diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs new file mode 100644 index 00000000000..d5bd4b60118 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -0,0 +1,59 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that regions which appear only in the closure's generics (in +// this case, `'a`) are properly mapped to the creator's generics. In +// this case, the closure constrains its type parameter `T` to outlive +// the same `'a` for which it implements `Trait`, which can only be the `'a` +// from the function definition. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +trait Trait<'a> {} + +fn establish_relationships(value: T, closure: F) +where + F: FnOnce(T), +{ + closure(value) +} + +fn require<'a, T>(t: T) +where + T: Trait<'a> + 'a, +{ +} + +#[rustc_regions] +fn supply<'a, T>(value: T) +where + T: Trait<'a>, +{ + establish_relationships(value, |value| { + // This function call requires that + // + // (a) T: Trait<'a> + // + // and + // + // (b) T: 'a + // + // The latter does not hold. + + require(value); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr new file mode 100644 index 00000000000..eb415ec8d1a --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -0,0 +1,60 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-from-trait-match.rs:53:9 + | +53 | require(value); + | ^^^^^^^ + +note: External requirements + --> $DIR/propagate-from-trait-match.rs:42:36 + | +42 | establish_relationships(value, |value| { + | ____________________________________^ +43 | | // This function call requires that +44 | | // +45 | | // (a) T: Trait<'a> +... | +55 | | //~| ERROR failed type test +56 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:16 ~ propagate_from_trait_match[317d]::supply[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((T,)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 56:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/propagate-from-trait-match.rs:42:36 + | +42 | establish_relationships(value, |value| { + | ____________________________________^ +43 | | // This function call requires that +44 | | // +45 | | // (a) T: Trait<'a> +... | +55 | | //~| ERROR failed type test +56 | | }); + | |_____^ + +note: No external requirements + --> $DIR/propagate-from-trait-match.rs:38:1 + | +38 | / fn supply<'a, T>(value: T) +39 | | where +40 | | T: Trait<'a>, +41 | | { +... | +56 | | }); +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 0d6bd42abb0b6082d23e7fe84b0121dfa0476b78 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 05:50:49 -0500 Subject: make `blame_span` deterministic --- .../borrow_check/nll/region_infer/mod.rs | 60 ++++++++++++++-------- src/test/compile-fail/mir_check_cast_closure.rs | 2 +- src/test/compile-fail/mir_check_cast_reify.rs | 2 +- src/test/compile-fail/mir_check_cast_unsafe_fn.rs | 2 +- .../propagate-approximated-fail-no-postdom.rs | 2 +- .../propagate-approximated-fail-no-postdom.stderr | 10 ++-- .../propagate-approximated-ref.stderr | 4 +- .../propagate-approximated-to-empty.stderr | 4 +- .../propagate-approximated-val.stderr | 4 +- .../propagate-from-trait-match.rs | 3 +- .../propagate-from-trait-match.stderr | 30 +++++------ 11 files changed, 71 insertions(+), 52 deletions(-) (limited to 'src') 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 f03e8bd7ac1..9a3076c0c32 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -872,35 +872,50 @@ impl<'tcx> RegionInferenceContext<'tcx> { // be obvious to the user -- not to mention the naive notion // of dependencies, which doesn't account for the locations of // contraints at all. But it will do for now. - for constraint in &self.constraints { - if constraint.sub == fr2 && influenced_fr1[constraint.sup] { - return constraint.span; - } - } - - bug!( - "could not find any constraint to blame for {:?}: {:?}", - fr1, - fr2 - ); + let relevant_constraint = self.constraints + .iter() + .filter_map(|constraint| { + if constraint.sub != fr2 { + None + } else { + influenced_fr1[constraint.sup] + .map(|distance| (distance, constraint.span)) + } + }) + .min() // constraining fr1 with fewer hops *ought* to be more obvious + .map(|(_dist, span)| span); + + relevant_constraint.unwrap_or_else(|| { + bug!( + "could not find any constraint to blame for {:?}: {:?}", + fr1, + fr2 + ); + }) } /// Finds all regions whose values `'a` may depend on in some way. - /// Basically if there exists a constraint `'a: 'b @ P`, then `'b` - /// and `dependencies('b)` will be in the final set. + /// For each region, returns either `None` (does not influence + /// `'a`) or `Some(d)` which indicates that it influences `'a` + /// with distinct `d` (minimum number of edges that must be + /// traversed). /// /// Used during error reporting, extremely naive and inefficient. - fn dependencies(&self, r0: RegionVid) -> IndexVec { - let mut result_set = IndexVec::from_elem(false, &self.definitions); + fn dependencies(&self, r0: RegionVid) -> IndexVec> { + let mut result_set = IndexVec::from_elem(None, &self.definitions); let mut changed = true; - result_set[r0] = true; + result_set[r0] = Some(0); // distance 0 from `r0` while changed { changed = false; for constraint in &self.constraints { - if result_set[constraint.sup] { - if !result_set[constraint.sub] { - result_set[constraint.sub] = true; + if let Some(n) = result_set[constraint.sup] { + let m = n + 1; + if result_set[constraint.sub] + .map(|distance| m < distance) + .unwrap_or(true) + { + result_set[constraint.sub] = Some(m); changed = true; } } @@ -1049,13 +1064,16 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi value: &T, ) -> T where - T: TypeFoldable<'tcx> + T: TypeFoldable<'tcx>, { infcx.tcx.fold_regions(value, &mut false, |r, _depth| { if let ty::ReClosureBound(vid) = r { closure_mapping[*vid] } else { - bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) + bug!( + "subst_closure_mapping: encountered non-closure bound free region {:?}", + r + ) } }) } diff --git a/src/test/compile-fail/mir_check_cast_closure.rs b/src/test/compile-fail/mir_check_cast_closure.rs index be0d4b13741..6562efeb6d8 100644 --- a/src/test/compile-fail/mir_check_cast_closure.rs +++ b/src/test/compile-fail/mir_check_cast_closure.rs @@ -14,9 +14,9 @@ fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 { let g: fn(_, _) -> _ = |_x, y| y; + //~^ ERROR free region `'b` does not outlive free region `'a` g //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `'b` does not outlive free region `'a` } fn main() {} diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/compile-fail/mir_check_cast_reify.rs index 091e0b71b2d..1736aea2d6d 100644 --- a/src/test/compile-fail/mir_check_cast_reify.rs +++ b/src/test/compile-fail/mir_check_cast_reify.rs @@ -45,8 +45,8 @@ fn bar<'a>(x: &'a u32) -> &'static u32 { // as part of checking the `ReifyFnPointer`. let f: fn(_) -> _ = foo; //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#1r` does not outlive free region `'static` f(x) - //~^ ERROR free region `'_#1r` does not outlive free region `'static` } fn main() {} diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs index 701a7c6b056..39eafa10040 100644 --- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs +++ b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs @@ -17,8 +17,8 @@ fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { // in `g`. These are related via the `UnsafeFnPointer` cast. let g: unsafe fn(_) -> _ = f; //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#1r` does not outlive free region `'static` unsafe { g(input) } - //~^ ERROR free region `'_#1r` does not outlive free region `'static` } fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs index c2f071cc029..50d7877de50 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs @@ -54,8 +54,8 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell // Only works if 'x: 'y: let p = x.get(); //~^ WARN not reporting region error due to -Znll + //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` demand_y(x, y, p) - //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r` }, ); } diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index cdda8ab5392..f90bc7c175a 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -5,10 +5,10 @@ warning: not reporting region error due to -Znll | ^^^^^^^ error: free region `'_#5r` does not outlive free region `'_#6r` - --> $DIR/propagate-approximated-fail-no-postdom.rs:57:25 + --> $DIR/propagate-approximated-fail-no-postdom.rs:55:17 | -57 | demand_y(x, y, p) - | ^ +55 | let p = x.get(); + | ^ note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9 @@ -17,8 +17,8 @@ note: No external requirements 54 | | // Only works if 'x: 'y: 55 | | let p = x.get(); 56 | | //~^ WARN not reporting region error due to -Znll -57 | | demand_y(x, y, p) -58 | | //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r` +57 | | //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` +58 | | demand_y(x, y, p) 59 | | }, | |_________^ | diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 717cf481a01..4bae29ad326 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -24,10 +24,10 @@ note: External requirements = note: where '_#1r: '_#2r error: free region `'_#1r` does not outlive free region `'_#2r` - --> $DIR/propagate-approximated-ref.rs:53:38 + --> $DIR/propagate-approximated-ref.rs:53:29 | 53 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ^^^^^^^ + | ^^^^^^^ note: No external requirements --> $DIR/propagate-approximated-ref.rs:52:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr index e8dc8a13f87..502b344c89e 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -5,10 +5,10 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#6r` does not outlive free region `'_#4r` - --> $DIR/propagate-approximated-to-empty.rs:41:21 + --> $DIR/propagate-approximated-to-empty.rs:41:18 | 41 | demand_y(x, y, x.get()) - | ^ + | ^ note: No external requirements --> $DIR/propagate-approximated-to-empty.rs:39:47 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 43464bfb2b9..43d61fdf1b5 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -24,10 +24,10 @@ note: External requirements = note: where '_#1r: '_#2r error: free region `'_#1r` does not outlive free region `'_#2r` - --> $DIR/propagate-approximated-val.rs:46:37 + --> $DIR/propagate-approximated-val.rs:46:29 | 46 | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { - | ^^^^^^ + | ^^^^^^ note: No external requirements --> $DIR/propagate-approximated-val.rs:45:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs index d5bd4b60118..a5be2b43f04 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -40,6 +40,8 @@ where T: Trait<'a>, { establish_relationships(value, |value| { + //~^ ERROR failed type test + // This function call requires that // // (a) T: Trait<'a> @@ -52,7 +54,6 @@ where require(value); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test }); } diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index eb415ec8d1a..e81c45ef7ed 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -1,7 +1,7 @@ warning: not reporting region error due to -Znll - --> $DIR/propagate-from-trait-match.rs:53:9 + --> $DIR/propagate-from-trait-match.rs:55:9 | -53 | require(value); +55 | require(value); | ^^^^^^^ note: External requirements @@ -9,12 +9,12 @@ note: External requirements | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | // This function call requires that -44 | | // -45 | | // (a) T: Trait<'a> +43 | | //~^ ERROR failed type test +44 | | +45 | | // This function call requires that ... | -55 | | //~| ERROR failed type test -56 | | }); +56 | | //~^ WARNING not reporting region error due to -Znll +57 | | }); | |_____^ | = note: defining type: DefId(0/1:16 ~ propagate_from_trait_match[317d]::supply[0]::{{closure}}[0]) with closure substs [ @@ -26,17 +26,17 @@ note: External requirements = note: number of external vids: 2 = note: where T: '_#1r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 56:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 57:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } --> $DIR/propagate-from-trait-match.rs:42:36 | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | // This function call requires that -44 | | // -45 | | // (a) T: Trait<'a> +43 | | //~^ ERROR failed type test +44 | | +45 | | // This function call requires that ... | -55 | | //~| ERROR failed type test -56 | | }); +56 | | //~^ WARNING not reporting region error due to -Znll +57 | | }); | |_____^ note: No external requirements @@ -47,8 +47,8 @@ note: No external requirements 40 | | T: Trait<'a>, 41 | | { ... | -56 | | }); -57 | | } +57 | | }); +58 | | } | |_^ | = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [ -- cgit 1.4.1-3-g733a5 From 0f8ef0ca1a298619001998fcb5c329159db206c7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 17:11:16 -0500 Subject: more concise debug output when dumping the value of a region --- .../borrow_check/nll/region_infer/values.rs | 63 +++++++++++++++++++--- src/test/mir-opt/nll/named-lifetimes-basic.rs | 18 +++---- src/test/mir-opt/nll/reborrow-basic.rs | 4 +- src/test/mir-opt/nll/region-liveness-basic.rs | 4 +- .../mir-opt/nll/region-liveness-drop-may-dangle.rs | 2 +- .../nll/region-liveness-drop-no-may-dangle.rs | 2 +- .../nll/region-liveness-two-disjoint-uses.rs | 6 +-- src/test/mir-opt/nll/region-subtyping-basic.rs | 6 +-- 8 files changed, 77 insertions(+), 28 deletions(-) (limited to 'src') 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 849ccd3259a..5f23a0e5790 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -215,8 +215,11 @@ impl RegionValues { // FIXME. We could optimize this by improving // `BitMatrix::merge` so it does not always merge an entire // row. - debug!("add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", - from_region, to_region); + debug!( + "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", + from_region, + to_region + ); let mut changed = false; for elem in self.elements.all_universal_region_indices() { if self.contains(from_region, elem) { @@ -269,24 +272,70 @@ impl RegionValues { let mut result = String::new(); result.push_str("{"); - for (index, element) in self.elements_contained_in(r).enumerate() { - if index > 0 { - result.push_str(", "); - } + // Set to Some(l1, l2) when we have observed all the locations + // from l1..=l2 (inclusive) but not yet printed them. This + // gets extended if we then see l3 where l3 is the successor + // to l2. + let mut open_location: Option<(Location, Location)> = None; + + let mut sep = ""; + let mut push_sep = |s: &mut String| { + s.push_str(sep); + sep = ", "; + }; + for element in self.elements_contained_in(r) { match element { RegionElement::Location(l) => { - result.push_str(&format!("{:?}", l)); + if let Some((location1, location2)) = open_location { + if location2.block == l.block + && location2.statement_index == l.statement_index - 1 + { + open_location = Some((location1, l)); + continue; + } + + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + } + + open_location = Some((l, l)); } RegionElement::UniversalRegion(fr) => { + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + open_location = None; + } + + push_sep(&mut result); result.push_str(&format!("{:?}", fr)); } } } + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + } + result.push_str("}"); result } + + fn push_location_range(str: &mut String, location1: Location, location2: Location) { + if location1 == location2 { + str.push_str(&format!("{:?}", location1)); + } else { + assert_eq!(location1.block, location2.block); + str.push_str(&format!( + "{:?}[{}..={}]", + location1.block, + location1.statement_index, + location2.statement_index + )); + } + } } diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index f14979f9733..8feac15d69a 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -34,15 +34,15 @@ fn main() { // | '_#4r | Local | ['_#4r] // | // | Inferred Region Values -// | '_#0r | {'_#0r, bb0[0], bb0[1]} -// | '_#1r | {'_#1r, bb0[0], bb0[1]} -// | '_#2r | {'_#2r, bb0[0], bb0[1]} -// | '_#3r | {'_#3r, bb0[0], bb0[1]} -// | '_#4r | {'_#4r, bb0[0], bb0[1]} -// | '_#5r | {'_#1r, bb0[0], bb0[1]} -// | '_#6r | {'_#2r, bb0[0], bb0[1]} -// | '_#7r | {'_#1r, bb0[0], bb0[1]} -// | '_#8r | {'_#3r, bb0[0], bb0[1]} +// | '_#0r | {'_#0r, bb0[0..=1]} +// | '_#1r | {'_#1r, bb0[0..=1]} +// | '_#2r | {'_#2r, bb0[0..=1]} +// | '_#3r | {'_#3r, bb0[0..=1]} +// | '_#4r | {'_#4r, bb0[0..=1]} +// | '_#5r | {'_#1r, bb0[0..=1]} +// | '_#6r | {'_#2r, bb0[0..=1]} +// | '_#7r | {'_#1r, bb0[0..=1]} +// | '_#8r | {'_#3r, bb0[0..=1]} // | // ... // fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool { diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index 9e6d6aaee15..f69c51c3562 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,9 +28,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#7r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#7r | {bb0[6..=14]} // ... -// | '_#9r | {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#9r | {bb0[11..=14]} // ... // let _2: &'_#7r mut i32; // ... diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index ab99d79d7f3..e9834305550 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,8 +31,8 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} -// | '_#3r | {bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=1], bb3[0..=1]} +// | '_#3r | {bb2[1], bb3[0..=1]} // ... // let _2: &'_#3r usize; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index 515772a9427..c14ce6bb581 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,7 +44,7 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]} +// | '_#6r | {bb2[3..=5], bb3[0..=1]} // ... // let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index a257910b0b8..058a57fe612 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,7 +46,7 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#6r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]} +// | '_#6r | {bb2[3..=5], bb3[0..=2], bb4[0], bb5[0..=2], bb6[0], bb7[0..=1], bb8[0]} // ... // let _2: Wrap<&'_#6r usize>; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index 3041c2cb061..821cd736671 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -36,10 +36,10 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=1], bb3[0..=1]} // ... -// | '_#4r | {bb8[1], bb8[2], bb8[3], bb8[4]} -// | '_#5r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} +// | '_#4r | {bb8[1..=4]} +// | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]} // ... // let mut _2: &'_#5r usize; // ... diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 5a3f831331c..a3f68ed5ebf 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,9 +32,9 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#2r | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} -// | '_#3r | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]} -// | '_#4r | {bb2[5], bb2[6], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=6], bb3[0..=1]} +// | '_#3r | {bb2[1..=6], bb3[0..=1]} +// | '_#4r | {bb2[5..=6], bb3[0..=1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // let _2: &'_#3r usize; -- cgit 1.4.1-3-g733a5 From 4f43c5b1e78115083254e6df12e0d34f7da9748b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 15 Dec 2017 17:04:48 -0500 Subject: stop dumping DefPath into "failed type test" errors The prior messages were not stable across platforms. --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 6 +++++- .../nll/closure-requirements/propagate-from-trait-match.rs | 2 +- .../closure-requirements/propagate-from-trait-match.stderr | 6 +++--- .../ui/nll/ty-outlives/projection-no-regions-closure.rs | 4 ++-- .../nll/ty-outlives/projection-no-regions-closure.stderr | 8 ++++---- src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs | 4 ++-- .../ui/nll/ty-outlives/projection-no-regions-fn.stderr | 4 ++-- .../ui/nll/ty-outlives/projection-one-region-closure.rs | 6 +++--- .../nll/ty-outlives/projection-one-region-closure.stderr | 6 +++--- .../projection-two-region-trait-bound-closure.rs | 6 +++--- .../projection-two-region-trait-bound-closure.stderr | 12 ++++++------ .../ty-param-closure-outlives-from-return-type.rs | 4 ++-- .../ty-param-closure-outlives-from-return-type.stderr | 6 +++--- .../ty-param-closure-outlives-from-where-clause.rs | 4 ++-- .../ty-param-closure-outlives-from-where-clause.stderr | 14 +++++++------- src/test/ui/nll/ty-outlives/ty-param-fn-body.rs | 2 +- src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr | 2 +- src/test/ui/nll/ty-outlives/ty-param-fn.rs | 4 ++-- src/test/ui/nll/ty-outlives/ty-param-fn.stderr | 4 ++-- 19 files changed, 54 insertions(+), 50 deletions(-) (limited to 'src') 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 9a3076c0c32..36b59d9fb7d 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -466,7 +466,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Oh the humanity. Obviously we will do better than this error eventually. tcx.sess.span_err( type_test.span, - &format!("failed type test: {:?}", type_test), + &format!( + "`{}` does not outlive `{:?}`", + type_test.generic_kind, + type_test.lower_bound, + ), ); } } diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs index a5be2b43f04..604c81da49d 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -40,7 +40,7 @@ where T: Trait<'a>, { establish_relationships(value, |value| { - //~^ ERROR failed type test + //~^ ERROR `T` does not outlive // This function call requires that // diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index e81c45ef7ed..efac55f2bee 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -9,7 +9,7 @@ note: External requirements | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | //~^ ERROR failed type test +43 | | //~^ ERROR `T` does not outlive 44 | | 45 | | // This function call requires that ... | @@ -26,12 +26,12 @@ note: External requirements = note: number of external vids: 2 = note: where T: '_#1r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/propagate-from-trait-match.rs:42:36: 57:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#3r` --> $DIR/propagate-from-trait-match.rs:42:36 | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | //~^ ERROR failed type test +43 | | //~^ ERROR `T` does not outlive 44 | | 45 | | // This function call requires that ... | diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs index b91c01fb671..9451163ace9 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -35,7 +35,7 @@ where { with_signature(x, |mut y| Box::new(y.next())) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } #[rustc_regions] @@ -53,7 +53,7 @@ where { with_signature(x, |mut y| Box::new(y.next())) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 1d124f2d49a..9afd5d41182 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -72,7 +72,7 @@ note: External requirements = note: number of external vids: 4 = note: where ::Item: '_#3r -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:36:23: 36:49, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `::Item` does not outlive `'_#4r` --> $DIR/projection-no-regions-closure.rs:36:23 | 36 | with_signature(x, |mut y| Box::new(y.next())) @@ -86,7 +86,7 @@ note: No external requirements 34 | | T: Iterator, 35 | | { ... | -38 | | //~| ERROR failed type test +38 | | //~| ERROR `::Item` does not outlive 39 | | } | |_^ | @@ -111,7 +111,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-no-regions-closure.rs:54:23: 54:49, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `::Item` does not outlive `'_#6r` --> $DIR/projection-no-regions-closure.rs:54:23 | 54 | with_signature(x, |mut y| Box::new(y.next())) @@ -125,7 +125,7 @@ note: No external requirements 52 | | T: 'b + Iterator, 53 | | { ... | -56 | | //~| ERROR failed type test +56 | | //~| ERROR `::Item` does not outlive 57 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs index b7822eb259b..c815fdc1a0c 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -23,7 +23,7 @@ where { Box::new(x.next()) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } fn correct_region<'a, T>(mut x: T) -> Box @@ -39,7 +39,7 @@ where { Box::new(x.next()) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `::Item` does not outlive } fn outlives_region<'a, 'b, T>(mut x: T) -> Box diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr index 5c3bd04f3b1..4d13972641c 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `::Item` does not outlive `'_#4r` --> $DIR/projection-no-regions-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1697 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#5r, point: bb5[0], span: $DIR/projection-no-regions-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `::Item` does not outlive `'_#5r` --> $DIR/projection-no-regions-fn.rs:40:5 | 40 | Box::new(x.next()) diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs index cd9b1c2a8ce..e2a2d20d77d 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -55,7 +55,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` } @@ -67,7 +67,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` } @@ -89,7 +89,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` } diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index d187a094ec6..cbd80d70bf9 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -83,7 +83,7 @@ note: External requirements = note: where T: '_#3r = note: where '_#2r: '_#3r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:56:29: 56:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } +error: `T` does not outlive `'_#5r` --> $DIR/projection-one-region-closure.rs:56:29 | 56 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -112,7 +112,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:68:29: 68:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } +error: `T` does not outlive `'_#6r` --> $DIR/projection-one-region-closure.rs:68:29 | 68 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -142,7 +142,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#6r, point: bb0[5], span: $DIR/projection-one-region-closure.rs:90:29: 90:55, test: IsOutlivedByAnyRegionIn(['_#3r]) } +error: `T` does not outlive `'_#6r` --> $DIR/projection-one-region-closure.rs:90:29 | 90 | with_signature(cell, t, |cell, t| require(cell, t)); diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs index f8f3065fff4..42bfdfcf9f9 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -48,7 +48,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `>::AssocType` does not outlive `'_#7r` } #[rustc_regions] @@ -59,7 +59,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `>::AssocType` does not outlive `'_#8r` } #[rustc_regions] @@ -80,7 +80,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `>::AssocType` does not outlive `'_#8r` } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index 02650214129..5b708a0d7e6 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -152,7 +152,7 @@ note: External requirements = note: number of external vids: 3 = note: where >::AssocType: '_#2r -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#5r, '_#6r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#7r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:49:29: 49:55, test: Any([IsOutlivedByAnyRegionIn(['_#6r, '_#5r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#5r, '_#6r])])]) } +error: `>::AssocType` does not outlive `'_#7r` --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 | 49 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -166,7 +166,7 @@ note: No external requirements 47 | | T: Anything<'b, 'c>, 48 | | { ... | -51 | | //~| ERROR failed type test +51 | | //~| ERROR `>::AssocType` does not outlive `'_#7r` 52 | | } | |_^ | @@ -176,7 +176,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:60:29: 60:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } +error: `>::AssocType` does not outlive `'_#8r` --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 | 60 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -190,7 +190,7 @@ note: No external requirements 57 | | T: Anything<'b, 'c>, 58 | | 'a: 'a, ... | -62 | | //~| ERROR failed type test +62 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` 63 | | } | |_^ | @@ -201,7 +201,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T, '_#6r, '_#7r]), item_def_id: DefId(0/0:5 ~ projection_two_region_trait_bound_closure[317d]::Anything[0]::AssocType[0]) }, lower_bound: '_#8r, point: bb0[5], span: $DIR/projection-two-region-trait-bound-closure.rs:81:29: 81:55, test: Any([IsOutlivedByAnyRegionIn(['_#7r, '_#6r]), All([IsOutlivedByAnyRegionIn(['_#4r]), IsOutlivedByAllRegionsIn(['_#6r, '_#7r])])]) } +error: `>::AssocType` does not outlive `'_#8r` --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 | 81 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -215,7 +215,7 @@ note: No external requirements 68 | | T: Anything<'b, 'c>, 69 | | T::AssocType: 'a, ... | -83 | | //~| ERROR failed type test +83 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` 84 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs index 6b23c82c771..14e2eb26976 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -36,7 +36,7 @@ where with_signature(x, |y| y) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn correct_region<'a, T>(x: Box) -> Box @@ -52,7 +52,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn outlives_region<'a, 'b, T>(x: Box) -> Box diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr index 721896a93ff..37ebc38da4d 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -25,7 +25,7 @@ note: External requirements = note: number of external vids: 3 = note: where T: '_#2r -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#4r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-return-type.rs:37:23: 37:28, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 | 37 | with_signature(x, |y| y) @@ -39,7 +39,7 @@ note: No external requirements 28 | | T: Debug, 29 | | { ... | -39 | | //~| ERROR failed type test +39 | | //~| ERROR `T` does not outlive 40 | | } | |_^ | @@ -48,7 +48,7 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-closure-outlives-from-return-type.rs:53:5: 53:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 | 53 | x diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs index 54f7b4fa50d..beed1a740ea 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -36,7 +36,7 @@ where #[rustc_regions] fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { with_signature(a, b, |x, y| { - //~^ ERROR failed type test + //~^ ERROR `T` does not outlive // // See `correct_region`, which explains the point of this // test. The only difference is that, in the case of this @@ -74,7 +74,7 @@ where T: 'b, { with_signature(a, b, |x, y| { - //~^ ERROR failed type test + //~^ ERROR `T` does not outlive // See `correct_region` require(&x, &y) //~^ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr index 748333badce..78445eb47c3 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -15,7 +15,7 @@ note: External requirements | 38 | with_signature(a, b, |x, y| { | __________________________^ -39 | | //~^ ERROR failed type test +39 | | //~^ ERROR `T` does not outlive 40 | | // 41 | | // See `correct_region`, which explains the point of this ... | @@ -58,7 +58,7 @@ note: External requirements | 76 | with_signature(a, b, |x, y| { | __________________________^ -77 | | //~^ ERROR failed type test +77 | | //~^ ERROR `T` does not outlive 78 | | // See `correct_region` 79 | | require(&x, &y) 80 | | //~^ WARNING not reporting region error due to -Znll @@ -94,12 +94,12 @@ note: External requirements = note: number of external vids: 4 = note: where T: '_#3r -error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#3r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26: 47:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#3r` --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 | 38 | with_signature(a, b, |x, y| { | __________________________^ -39 | | //~^ ERROR failed type test +39 | | //~^ ERROR `T` does not outlive 40 | | // 41 | | // See `correct_region`, which explains the point of this ... | @@ -112,7 +112,7 @@ note: No external requirements | 37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { 38 | | with_signature(a, b, |x, y| { -39 | | //~^ ERROR failed type test +39 | | //~^ ERROR `T` does not outlive 40 | | // ... | 47 | | }) @@ -140,12 +140,12 @@ note: No external requirements T ] -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26: 81:6, test: IsOutlivedByAnyRegionIn(['_#1r, '_#3r]) } +error: `T` does not outlive `'_#5r` --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 | 76 | with_signature(a, b, |x, y| { | __________________________^ -77 | | //~^ ERROR failed type test +77 | | //~^ ERROR `T` does not outlive 78 | | // See `correct_region` 79 | | require(&x, &y) 80 | | //~^ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs index 42e62b23896..a1e636cbc44 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -29,7 +29,7 @@ fn region_within_body(t: T) { fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { outlives(cell, t) //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn outlives<'a, T>(x: Cell<&'a usize>, y: T) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr index 3cc335bb29d..bbe55c52b6e 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 30 | outlives(cell, t) | ^^^^^^^^ -error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#4r, point: bb0[4], span: $DIR/ty-param-fn-body.rs:30:5: 30:22, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-fn-body.rs:30:5 | 30 | outlives(cell, t) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs index c6547ae68fa..76783af4ceb 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -21,7 +21,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn correct_region<'a, T>(x: Box) -> Box @@ -37,7 +37,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR failed type test + //~| ERROR `T` does not outlive } fn outlives_region<'a, 'b, T>(x: Box) -> Box diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr index 426bece3e9d..02c4ebbd5ac 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -10,13 +10,13 @@ warning: not reporting region error due to -Znll 38 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#3r, point: bb0[3], span: $DIR/ty-param-fn.rs:22:5: 22:6, test: IsOutlivedByAnyRegionIn(['_#2r]) } +error: `T` does not outlive `'_#3r` --> $DIR/ty-param-fn.rs:22:5 | 22 | x | ^ -error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-fn.rs:38:5: 38:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) } +error: `T` does not outlive `'_#4r` --> $DIR/ty-param-fn.rs:38:5 | 38 | x -- cgit 1.4.1-3-g733a5 From 03bfb0f31670364a47ddf14db068bf47e291e6ba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:18:15 -0500 Subject: tweak comment on `TypeTest` to be more accurate --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') 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 36b59d9fb7d..e2a36e6863e 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -122,10 +122,12 @@ pub struct Constraint { /// checks that they meet certain extra criteria. If not, an error /// can be issued. /// -/// One reason for this is that these type tests always boil down to a -/// check like `'a: 'x` where `'a` is a universally quantified region -/// -- and therefore not one whose value is really meant to be -/// *inferred*, precisely. Another reason is that these type tests can +/// One reason for this is that these type tests typically boil down +/// to a check like `'a: 'x` where `'a` is a universally quantified +/// region -- and therefore not one whose value is really meant to be +/// *inferred*, precisely (this is not always the case: one can have a +/// type test like `>::Bar: 'x`, where `'?0` is an +/// inference variable). Another reason is that these type tests can /// involve *disjunction* -- that is, they can be satisfied in more /// than one way. /// -- cgit 1.4.1-3-g733a5 From 3c56c3610ec7684f4c759c3eee95a50fef208e1b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:21:57 -0500 Subject: fix comment on `check_type_tests` --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'src') 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 e2a36e6863e..58e16e7673a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -432,18 +432,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Once regions have been propagated, this method is used to see - /// whether any of the constraints were too strong. In particular, - /// we want to check for a case where a universally quantified - /// region exceeded its bounds. Consider: - /// - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// - /// In this case, returning `x` requires `&'a u32 <: &'b u32` - /// and hence we establish (transitively) a constraint that - /// `'a: 'b`. The `propagate_constraints` code above will - /// therefore add `end('a)` into the region for `'b` -- but we - /// have no evidence that `'b` outlives `'a`, so we want to report - /// an error. + /// whether the "type tests" produced by typeck were satisfied; + /// type tests encode type-outlives relationships like `T: + /// 'a`. See `TypeTest` for more details. fn check_type_tests<'gcx>( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, -- cgit 1.4.1-3-g733a5 From 3d826e5681770a2dfee4b980173f383b8201e844 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:26:56 -0500 Subject: remove dead `is_foo_free_region` helpers Only `is_local_free_region` is used. --- src/librustc_mir/borrow_check/nll/universal_regions.rs | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 99beae13bd9..64195338aa5 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -249,23 +249,6 @@ impl<'tcx> UniversalRegions<'tcx> { (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new) } - /// True if `r` is classified as a global region. - pub fn is_global_free_region(&self, r: RegionVid) -> bool { - self.region_classification(r) == Some(RegionClassification::Global) - } - - /// True if `r` is classified as an external region. - pub fn is_extern_free_region(&self, r: RegionVid) -> bool { - self.region_classification(r) == Some(RegionClassification::External) - } - - /// True if `r` is a free region that is classified as global or - /// extern. This is an important category, because these regions - /// can be referenced in `ClosureRegionRequirements`. - pub fn is_non_local_free_region(&self, r: RegionVid) -> bool { - self.region_classification(r) == Some(RegionClassification::Local) - } - /// True if `r` is classified as an local region. pub fn is_local_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Local) -- cgit 1.4.1-3-g733a5 From 1816ede386c6dd6e61f50e7b0f9bdba19adc0e24 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 19 Dec 2017 04:28:45 -0500 Subject: be specific about what kind of normalization we mean --- src/librustc_mir/borrow_check/nll/universal_regions.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 64195338aa5..a1e6ea135c6 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -71,12 +71,14 @@ pub struct UniversalRegions<'tcx> { /// The return type of this function, with all regions replaced by /// their universal `RegionVid` equivalents. This type is **NOT - /// NORMALIZED**. + /// NORMALIZED** (i.e., it contains unnormalized associated type + /// projections). pub output_ty: Ty<'tcx>, /// The fully liberated input types of this function, with all /// regions replaced by their universal `RegionVid` equivalents. - /// This type is **NOT NORMALIZED**. + /// This type is **NOT NORMALIZED** (i.e., it contains + /// unnormalized associated type projections). pub input_tys: &'tcx [Ty<'tcx>], /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to -- cgit 1.4.1-3-g733a5