diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index c477a0d383e21..541c1356dd4ab 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -1067,6 +1067,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub_region: Region<'tcx>, sup_origin: SubregionOrigin<'tcx>, sup_region: Region<'tcx>) { + let mut err = self.report_inference_failure(var_origin); self.tcx.note_and_explain_region(region_scope_tree, &mut err, diff --git a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs index d4ea899dc747f..7b0f2933580b3 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -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 { - 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)?; diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs index edc38b6bb14ee..59b36a50a2b09 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs @@ -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>, + 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 { 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"), + } } } diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 5617c77238597..c106fd0c3d2c0 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -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 { - let NiceRegionError { span, sub, sup, .. } = *self; + let (span, sub, sup) = self.get_regions(); debug!( "try_report_named_anon_conflict(sub={:?}, sup={:?})", diff --git a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs new file mode 100644 index 0000000000000..95f44b813c5d2 --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs @@ -0,0 +1,124 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! 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) 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 { + 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 origin_sp == sup_sp || origin_sp.contains(sup_sp) { +// // sup_sp == origin.span(): +// +// 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 +// +// // origin.contains(&sup_sp): +// +// let mut f: Option<&u32> = None; +// ----- borrowed data cannot be stored into here... +// closure_expecting_bound(|x: &'x u32| { +// ------------ ... because it cannot outlive this closure +// f = Some(x); +// ^ cannot be stored outside of its closure + err.span_label(*external_span, + "borrowed data cannot be stored into here..."); + err.span_label(*closure_span, + "...because it cannot outlive this closure"); + } else { +// FIXME: the wording for this case could be much improved +// +// let mut lines_to_use: Vec<&CrateId> = Vec::new(); +// - cannot infer an appropriate lifetime... +// let push_id = |installed_id: &CrateId| { +// ------- ------------------------ borrowed data cannot outlive this closure +// | +// ...so that variable is valid at time of its declaration +// lines_to_use.push(installed_id); +// ^^^^^^^^^^^^ cannot be stored outside of its closure + err.span_label(origin_sp, + "cannot infer an appropriate lifetime..."); + err.span_label(*external_span, + "...so that variable is valid at time of its \ + declaration"); + err.span_label(*closure_span, + "borrowed data cannot outlive this closure"); + } + err.emit(); + return Some(ErrorReported); + } + _ => {} + } + } + } + } + None + } +} + 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 da136a34b9971..9a2f98d4622f7 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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; } diff --git a/src/test/ui/borrowck/issue-45983.rs b/src/test/ui/borrowck/issue-45983.rs new file mode 100644 index 0000000000000..a6e5067195f47 --- /dev/null +++ b/src/test/ui/borrowck/issue-45983.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn give_any FnOnce(&'r ())>(f: F) { + f(&()); +} + +fn main() { + let x = None; + give_any(|y| x = Some(y)); + //~^ ERROR borrowed data cannot be stored outside of its closure +} diff --git a/src/test/ui/borrowck/issue-45983.stderr b/src/test/ui/borrowck/issue-45983.stderr new file mode 100644 index 0000000000000..496f15c289c17 --- /dev/null +++ b/src/test/ui/borrowck/issue-45983.stderr @@ -0,0 +1,12 @@ +error: borrowed data cannot be stored outside of its closure + --> $DIR/issue-45983.rs:17:27 + | +16 | let x = None; + | - borrowed data cannot be stored into here... +17 | give_any(|y| x = Some(y)); + | --- ^ cannot be stored outside of its closure + | | + | ...because it cannot outlive this closure + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-7573.rs b/src/test/ui/borrowck/issue-7573.rs similarity index 75% rename from src/test/compile-fail/issue-7573.rs rename to src/test/ui/borrowck/issue-7573.rs index d13da1d9fd948..8f1545fa00986 100644 --- a/src/test/compile-fail/issue-7573.rs +++ b/src/test/ui/borrowck/issue-7573.rs @@ -24,9 +24,14 @@ impl CrateId { } pub fn remove_package_from_database() { - let mut lines_to_use: Vec<&CrateId> = Vec::new(); //~ ERROR E0495 + let mut lines_to_use: Vec<&CrateId> = Vec::new(); + //~^ NOTE cannot infer an appropriate lifetime let push_id = |installed_id: &CrateId| { + //~^ NOTE borrowed data cannot outlive this closure + //~| NOTE ...so that variable is valid at time of its declaration lines_to_use.push(installed_id); + //~^ ERROR borrowed data cannot be stored outside of its closure + //~| NOTE cannot be stored outside of its closure }; list_database(push_id); diff --git a/src/test/ui/borrowck/issue-7573.stderr b/src/test/ui/borrowck/issue-7573.stderr new file mode 100644 index 0000000000000..99b48d9276c06 --- /dev/null +++ b/src/test/ui/borrowck/issue-7573.stderr @@ -0,0 +1,16 @@ +error: borrowed data cannot be stored outside of its closure + --> $DIR/issue-7573.rs:32: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 + | | + | ...so that variable is valid at time of its declaration +... +32 | lines_to_use.push(installed_id); + | ^^^^^^^^^^^^ cannot be stored outside of its closure + +error: aborting due to previous error + diff --git a/src/test/compile-fail/regions-escape-bound-fn-2.rs b/src/test/ui/borrowck/regions-escape-bound-fn-2.rs similarity index 89% rename from src/test/compile-fail/regions-escape-bound-fn-2.rs rename to src/test/ui/borrowck/regions-escape-bound-fn-2.rs index 1329d05a0f690..1c38dee99a7b0 100644 --- a/src/test/compile-fail/regions-escape-bound-fn-2.rs +++ b/src/test/ui/borrowck/regions-escape-bound-fn-2.rs @@ -16,5 +16,5 @@ fn with_int(f: F) where F: FnOnce(&isize) { fn main() { let mut x = None; with_int(|y| x = Some(y)); - //~^ ERROR cannot infer + //~^ ERROR borrowed data cannot be stored outside of its closure } diff --git a/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr b/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr new file mode 100644 index 0000000000000..3d88f4fd52e2e --- /dev/null +++ b/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr @@ -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 + diff --git a/src/test/compile-fail/regions-escape-bound-fn.rs b/src/test/ui/borrowck/regions-escape-bound-fn.rs similarity index 85% rename from src/test/compile-fail/regions-escape-bound-fn.rs rename to src/test/ui/borrowck/regions-escape-bound-fn.rs index 02e62ffddfd50..c22742371acbc 100644 --- a/src/test/compile-fail/regions-escape-bound-fn.rs +++ b/src/test/ui/borrowck/regions-escape-bound-fn.rs @@ -15,5 +15,6 @@ fn with_int(f: F) where F: FnOnce(&isize) { fn main() { let mut x: Option<&isize> = None; - with_int(|y| x = Some(y)); //~ ERROR cannot infer + with_int(|y| x = Some(y)); + //~^ ERROR borrowed data cannot be stored outside of its closure } diff --git a/src/test/ui/borrowck/regions-escape-bound-fn.stderr b/src/test/ui/borrowck/regions-escape-bound-fn.stderr new file mode 100644 index 0000000000000..a2ad7c3f768c6 --- /dev/null +++ b/src/test/ui/borrowck/regions-escape-bound-fn.stderr @@ -0,0 +1,12 @@ +error: borrowed data cannot be stored outside of its closure + --> $DIR/regions-escape-bound-fn.rs:18:27 + | +17 | let mut x: Option<&isize> = 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 + diff --git a/src/test/compile-fail/regions-escape-unboxed-closure.rs b/src/test/ui/borrowck/regions-escape-unboxed-closure.rs similarity index 84% rename from src/test/compile-fail/regions-escape-unboxed-closure.rs rename to src/test/ui/borrowck/regions-escape-unboxed-closure.rs index cf41fad270839..5a214504df4c1 100644 --- a/src/test/compile-fail/regions-escape-unboxed-closure.rs +++ b/src/test/ui/borrowck/regions-escape-unboxed-closure.rs @@ -13,5 +13,6 @@ fn with_int(f: &mut FnMut(&isize)) { fn main() { let mut x: Option<&isize> = None; - with_int(&mut |y| x = Some(y)); //~ ERROR cannot infer + with_int(&mut |y| x = Some(y)); + //~^ ERROR borrowed data cannot be stored outside of its closure } diff --git a/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr b/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr new file mode 100644 index 0000000000000..4b01e42fa67d0 --- /dev/null +++ b/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr @@ -0,0 +1,12 @@ +error: borrowed data cannot be stored outside of its closure + --> $DIR/regions-escape-unboxed-closure.rs:16:32 + | +15 | let mut x: Option<&isize> = None; + | ----- borrowed data cannot be stored into here... +16 | with_int(&mut |y| x = Some(y)); + | --- ^ cannot be stored outside of its closure + | | + | ...because it cannot outlive this closure + +error: aborting due to previous error + diff --git a/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs b/src/test/ui/closure-expected-type/expect-region-supply-region.rs similarity index 90% rename from src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs rename to src/test/ui/closure-expected-type/expect-region-supply-region.rs index 9da12dc901fba..a464c5853e1c1 100644 --- a/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs +++ b/src/test/ui/closure-expected-type/expect-region-supply-region.rs @@ -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 E0495 + 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 E0495 + 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 cannot infer + //~^ ERROR borrowed data cannot be stored outside of its closure }); } diff --git a/src/test/ui/closure-expected-type/expect-region-supply-region.stderr b/src/test/ui/closure-expected-type/expect-region-supply-region.stderr new file mode 100644 index 0000000000000..ebb1e561e57ab --- /dev/null +++ b/src/test/ui/closure-expected-type/expect-region-supply-region.stderr @@ -0,0 +1,98 @@ +error: borrowed data cannot be stored outside of its closure + --> $DIR/expect-region-supply-region.rs:28:18 + | +26 | let mut f: Option<&u32> = None; + | ----- borrowed data cannot be stored into here... +27 | closure_expecting_bound(|x| { + | --- ...because it 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 + +error: borrowed data cannot be stored outside of its closure + --> $DIR/expect-region-supply-region.rs:38:18 + | +36 | let mut f: Option<&u32> = None; + | ----- borrowed data cannot be stored into here... +37 | closure_expecting_bound(|x: &u32| { + | --------- ...because it 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 + +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 + | +43 | let mut f: Option<&u32> = None; + | ----- borrowed data cannot be stored into here... +... +47 | closure_expecting_bound(|x: &'x u32| { + | ------------ ...because it cannot outlive this closure +... +52 | f = Some(x); + | ^ cannot be stored outside of its closure + +error: aborting due to 5 previous errors +