Move diagnostic logic to its own module

- Move specialized borrow checker diagnostic for bindings escaping its
  closure to its own module.
- Move affected tests to `ui`.
This commit is contained in:
Esteban Küber 2018-01-15 10:55:10 -08:00
parent 1a1afd74a6
commit 1820da5211
18 changed files with 289 additions and 63 deletions

View File

@ -66,7 +66,7 @@ use hir::map as hir_map;
use hir::def_id::DefId;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::error::TypeError;
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
@ -1068,42 +1068,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
sup_origin: SubregionOrigin<'tcx>,
sup_region: Region<'tcx>) {
// #45983: when trying to assign the contents of an argument to a binding outside of a
// closure, provide a specific message pointing this out.
if let (&SubregionOrigin::BindingTypeIsNotValidAtDecl(ref external_span),
&RegionKind::ReFree(ref free_region)) = (&sub_origin, sup_region) {
let hir = &self.tcx.hir;
if let Some(node_id) = hir.as_local_node_id(free_region.scope) {
match hir.get(node_id) {
hir_map::NodeExpr(hir::Expr {
node: hir::ExprClosure(_, _, _, closure_span, false),
..
}) => {
let sup_sp = sup_origin.span();
let origin_sp = var_origin.span();
let mut err = self.tcx.sess.struct_span_err(
sup_sp,
"borrowed data cannot be moved outside of its closure");
err.span_label(sup_sp, "cannot be moved outside of its closure");
if sup_sp == origin_sp {
err.span_label(*external_span,
"borrowed data cannot be moved into here...");
err.span_label(*closure_span,
"...because it cannot outlive this closure");
} else {
err.span_label(*closure_span,
"borrowed data cannot outlive this closure");
err.span_label(origin_sp,
"cannot infer an appropriate lifetime");
}
err.emit();
return;
}
_ => {}
}
}
}
let mut err = self.report_inference_failure(var_origin);
self.tcx.note_and_explain_region(region_scope_tree, &mut err,

View File

@ -53,7 +53,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
///
/// It will later be extended to trait objects.
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> {
let NiceRegionError { span, sub, sup, .. } = *self;
let (span, sub, sup) = self.get_regions();
// Determine whether the sub and sup consist of both anonymous (elided) regions.
let anon_reg_sup = self.is_suitable_region(sup)?;

View File

@ -18,46 +18,64 @@ use util::common::ErrorReported;
mod different_lifetimes;
mod find_anon_type;
mod named_anon_conflict;
mod outlives_closure;
mod util;
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
let (span, sub, sup) = match *error {
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup),
_ => return false, // inapplicable
};
match *error {
ConcreteFailure(..) | SubSupConflict(..) => {}
_ => return false, // inapplicable
}
if let Some(tables) = self.in_progress_tables {
let tables = tables.borrow();
NiceRegionError::new(self.tcx, span, sub, sup, Some(&tables)).try_report().is_some()
NiceRegionError::new(self.tcx, error.clone(), Some(&tables)).try_report().is_some()
} else {
NiceRegionError::new(self.tcx, span, sub, sup, None).try_report().is_some()
NiceRegionError::new(self.tcx, error.clone(), None).try_report().is_some()
}
}
}
pub struct NiceRegionError<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
span: Span,
sub: ty::Region<'tcx>,
sup: ty::Region<'tcx>,
error: Option<RegionResolutionError<'tcx>>,
regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
tables: Option<&'cx ty::TypeckTables<'tcx>>,
}
impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> {
pub fn new(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
error: RegionResolutionError<'tcx>,
tables: Option<&'cx ty::TypeckTables<'tcx>>,
) -> Self {
Self { tcx, error: Some(error), regions: None, tables }
}
pub fn new_from_span(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
span: Span,
sub: ty::Region<'tcx>,
sup: ty::Region<'tcx>,
tables: Option<&'cx ty::TypeckTables<'tcx>>,
) -> Self {
Self { tcx, span, sub, sup, tables }
Self { tcx, error: None, regions: Some((span, sub, sup)), tables }
}
pub fn try_report(&self) -> Option<ErrorReported> {
self.try_report_named_anon_conflict()
.or_else(|| self.try_report_anon_anon_conflict())
.or_else(|| self.try_report_outlives_closure())
}
pub fn get_regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
match (&self.error, self.regions) {
(&Some(ConcreteFailure(ref origin, sub, sup)), None) => (origin.span(), sub, sup),
(&Some(SubSupConflict(_, ref origin, sub, _, sup)), None) => (origin.span(), sub, sup),
(None, Some((span, sub, sup))) => (span, sub, sup),
(Some(_), Some(_)) => panic!("incorrectly built NiceRegionError"),
_ => panic!("trying to report on an incorrect lifetime failure"),
}
}
}

View File

@ -18,7 +18,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
/// When given a `ConcreteFailure` for a function with arguments containing a named region and
/// an anonymous region, emit an descriptive diagnostic error.
pub(super) fn try_report_named_anon_conflict(&self) -> Option<ErrorReported> {
let NiceRegionError { span, sub, sup, .. } = *self;
let (span, sub, sup) = self.get_regions();
debug!(
"try_report_named_anon_conflict(sub={:?}, sup={:?})",

View File

@ -0,0 +1,94 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Error Reporting for Anonymous Region Lifetime Errors
//! where both the regions are anonymous.
use infer::error_reporting::nice_region_error::NiceRegionError;
use infer::SubregionOrigin;
use ty::RegionKind;
use hir::{Expr, ExprClosure};
use hir::map::NodeExpr;
use util::common::ErrorReported;
use infer::lexical_region_resolve::RegionResolutionError::SubSupConflict;
impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
/// Print the error message for lifetime errors when binding excapes a closure.
///
/// Consider a case where we have
///
/// ```no_run
/// fn with_int<F>(f: F) where F: FnOnce(&isize) {
/// let x = 3;
/// f(&x);
/// }
/// fn main() {
/// let mut x = None;
/// with_int(|y| x = Some(y));
/// }
/// ```
///
/// the output will be
///
/// ```text
/// let mut x = None;
/// ----- borrowed data cannot be stored into here...
/// with_int(|y| x = Some(y));
/// --- ^ cannot be stored outside of its closure
/// |
/// ...because it cannot outlive this closure
/// ```
pub(super) fn try_report_outlives_closure(&self) -> Option<ErrorReported> {
if let Some(SubSupConflict(origin,
ref sub_origin,
_,
ref sup_origin,
sup_region)) = self.error {
// #45983: when trying to assign the contents of an argument to a binding outside of a
// closure, provide a specific message pointing this out.
if let (&SubregionOrigin::BindingTypeIsNotValidAtDecl(ref external_span),
&RegionKind::ReFree(ref free_region)) = (&sub_origin, sup_region) {
let hir = &self.tcx.hir;
if let Some(node_id) = hir.as_local_node_id(free_region.scope) {
match hir.get(node_id) {
NodeExpr(Expr {
node: ExprClosure(_, _, _, closure_span, false),
..
}) => {
let sup_sp = sup_origin.span();
let origin_sp = origin.span();
let mut err = self.tcx.sess.struct_span_err(
sup_sp,
"borrowed data cannot be stored outside of its closure");
err.span_label(sup_sp, "cannot be stored outside of its closure");
if sup_sp == origin_sp {
err.span_label(*external_span,
"borrowed data cannot be stored into here...");
err.span_label(*closure_span,
"...because it cannot outlive this closure");
} else {
err.span_label(*closure_span,
"borrowed data cannot outlive this closure");
err.span_label(origin_sp,
"cannot infer an appropriate lifetime");
}
err.emit();
return Some(ErrorReported);
}
_ => {}
}
}
}
}
None
}
}

View File

@ -989,7 +989,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
if let (Some(f), Some(o)) = (fr_name, outlived_fr_name) {
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
let nice = NiceRegionError::new(infcx.tcx, blame_span, o, f, Some(tables));
let nice = NiceRegionError::new_from_span(infcx.tcx, blame_span, o, f, Some(tables));
if let Some(ErrorReported) = nice.try_report() {
return;
}

View File

@ -15,5 +15,5 @@ fn give_any<F: for<'r> FnOnce(&'r ())>(f: F) {
fn main() {
let x = None;
give_any(|y| x = Some(y));
//~^ ERROR borrowed data cannot be moved outside of its closure
//~^ ERROR borrowed data cannot be stored outside of its closure
}

View File

@ -1,10 +1,10 @@
error: borrowed data cannot be moved outside of its closure
error: borrowed data cannot be stored outside of its closure
--> $DIR/issue-45983.rs:17:27
|
16 | let x = None;
| - borrowed data cannot be moved into here...
| - borrowed data cannot be stored into here...
17 | give_any(|y| x = Some(y));
| --- ^ cannot be moved outside of its closure
| --- ^ cannot be stored outside of its closure
| |
| ...because it cannot outlive this closure

View File

@ -29,8 +29,8 @@ pub fn remove_package_from_database() {
let push_id = |installed_id: &CrateId| {
//~^ NOTE borrowed data cannot outlive this closure
lines_to_use.push(installed_id);
//~^ ERROR borrowed data cannot be moved outside of its closure
//~| NOTE cannot be moved outside of its closure
//~^ ERROR borrowed data cannot be stored outside of its closure
//~| NOTE cannot be stored outside of its closure
};
list_database(push_id);

View File

@ -0,0 +1,14 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/issue-7573.rs:31:27
|
27 | let mut lines_to_use: Vec<&CrateId> = Vec::new();
| - cannot infer an appropriate lifetime
28 | //~^ NOTE cannot infer an appropriate lifetime
29 | let push_id = |installed_id: &CrateId| {
| ------------------------ borrowed data cannot outlive this closure
30 | //~^ NOTE borrowed data cannot outlive this closure
31 | lines_to_use.push(installed_id);
| ^^^^^^^^^^^^ cannot be stored outside of its closure
error: aborting due to previous error

View File

@ -16,5 +16,5 @@ fn with_int<F>(f: F) where F: FnOnce(&isize) {
fn main() {
let mut x = None;
with_int(|y| x = Some(y));
//~^ ERROR borrowed data cannot be moved outside of its closure
//~^ ERROR borrowed data cannot be stored outside of its closure
}

View File

@ -0,0 +1,12 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/regions-escape-bound-fn-2.rs:18:27
|
17 | let mut x = None;
| ----- borrowed data cannot be stored into here...
18 | with_int(|y| x = Some(y));
| --- ^ cannot be stored outside of its closure
| |
| ...because it cannot outlive this closure
error: aborting due to previous error

View File

@ -16,5 +16,5 @@ fn with_int<F>(f: F) where F: FnOnce(&isize) {
fn main() {
let mut x: Option<&isize> = None;
with_int(|y| x = Some(y));
//~^ ERROR borrowed data cannot be moved outside of its closure
//~^ ERROR borrowed data cannot be stored outside of its closure
}

View File

@ -0,0 +1,12 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/regions-escape-bound-fn.rs:18:27
|
18 | with_int(|y| x = Some(y));
| --- -----^-
| | | |
| | | cannot be stored outside of its closure
| | cannot infer an appropriate lifetime
| borrowed data cannot outlive this closure
error: aborting due to previous error

View File

@ -14,5 +14,5 @@ fn with_int(f: &mut FnMut(&isize)) {
fn main() {
let mut x: Option<&isize> = None;
with_int(&mut |y| x = Some(y));
//~^ ERROR borrowed data cannot be moved outside of its closure
//~^ ERROR borrowed data cannot be stored outside of its closure
}

View File

@ -0,0 +1,12 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/regions-escape-unboxed-closure.rs:16:32
|
16 | with_int(&mut |y| x = Some(y));
| --- -----^-
| | | |
| | | cannot be stored outside of its closure
| | cannot infer an appropriate lifetime
| borrowed data cannot outlive this closure
error: aborting due to previous error

View File

@ -25,7 +25,7 @@ fn expect_bound_supply_nothing() {
// it to escape into `f`:
let mut f: Option<&u32> = None;
closure_expecting_bound(|x| {
f = Some(x); //~ ERROR borrowed data cannot be moved outside of its closure
f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
});
}
@ -35,7 +35,7 @@ fn expect_bound_supply_bound() {
// closure:
let mut f: Option<&u32> = None;
closure_expecting_bound(|x: &u32| {
f = Some(x); //~ ERROR borrowed data cannot be moved outside of its closure
f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
});
}
@ -50,7 +50,7 @@ fn expect_bound_supply_named<'x>() {
// And we still cannot let `x` escape into `f`.
f = Some(x);
//~^ ERROR borrowed data cannot be moved outside of its closure
//~^ ERROR borrowed data cannot be stored outside of its closure
});
}

View File

@ -0,0 +1,100 @@
error: borrowed data cannot be stored outside of its closure
--> $DIR/expect-region-supply-region.rs:28:18
|
27 | closure_expecting_bound(|x| {
| --- borrowed data cannot outlive this closure
28 | f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
| -----^-
| | |
| | cannot be stored outside of its closure
| cannot infer an appropriate lifetime
error: borrowed data cannot be stored outside of its closure
--> $DIR/expect-region-supply-region.rs:38:18
|
37 | closure_expecting_bound(|x: &u32| {
| --------- borrowed data cannot outlive this closure
38 | f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
| -----^-
| | |
| | cannot be stored outside of its closure
| cannot infer an appropriate lifetime
error[E0308]: mismatched types
--> $DIR/expect-region-supply-region.rs:47:33
|
47 | closure_expecting_bound(|x: &'x u32| {
| ^^^^^^^ lifetime mismatch
|
= note: expected type `&u32`
found type `&'x u32`
note: the anonymous lifetime #2 defined on the body at 47:29...
--> $DIR/expect-region-supply-region.rs:47:29
|
47 | closure_expecting_bound(|x: &'x u32| {
| _____________________________^
48 | | //~^ ERROR mismatched types
49 | | //~| ERROR mismatched types
50 | |
... |
53 | | //~^ ERROR borrowed data cannot be stored outside of its closure
54 | | });
| |_____^
note: ...does not necessarily outlive the lifetime 'x as defined on the function body at 42:1
--> $DIR/expect-region-supply-region.rs:42:1
|
42 | / fn expect_bound_supply_named<'x>() {
43 | | let mut f: Option<&u32> = None;
44 | |
45 | | // Here we give a type annotation that `x` should be free. We get
... |
54 | | });
55 | | }
| |_^
error[E0308]: mismatched types
--> $DIR/expect-region-supply-region.rs:47:33
|
47 | closure_expecting_bound(|x: &'x u32| {
| ^^^^^^^ lifetime mismatch
|
= note: expected type `&u32`
found type `&'x u32`
note: the lifetime 'x as defined on the function body at 42:1...
--> $DIR/expect-region-supply-region.rs:42:1
|
42 | / fn expect_bound_supply_named<'x>() {
43 | | let mut f: Option<&u32> = None;
44 | |
45 | | // Here we give a type annotation that `x` should be free. We get
... |
54 | | });
55 | | }
| |_^
note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 47:29
--> $DIR/expect-region-supply-region.rs:47:29
|
47 | closure_expecting_bound(|x: &'x u32| {
| _____________________________^
48 | | //~^ ERROR mismatched types
49 | | //~| ERROR mismatched types
50 | |
... |
53 | | //~^ ERROR borrowed data cannot be stored outside of its closure
54 | | });
| |_____^
error: borrowed data cannot be stored outside of its closure
--> $DIR/expect-region-supply-region.rs:52:18
|
47 | closure_expecting_bound(|x: &'x u32| {
| ------------ borrowed data cannot outlive this closure
...
52 | f = Some(x);
| -----^-
| | |
| | cannot be stored outside of its closure
| cannot infer an appropriate lifetime
error: aborting due to 5 previous errors