From d10fe26f39983c782f1a37a447f2c10c2ef561ba Mon Sep 17 00:00:00 2001 From: Esteban Kuber Date: Sun, 10 Oct 2021 10:37:57 +0000 Subject: [PATCH] Point at capture points for non-`'static` reference crossing a `yield` point ``` error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/issue-72312.rs:10:24 | LL | pub async fn start(&self) { | ^^^^^ this data with an anonymous lifetime `'_`... ... LL | require_static(async move { | -------------- ...is required to live as long as `'static` here... LL | &self; | ----- ...and is captured here | note: `'static` lifetime requirement introduced by this trait bound --> $DIR/issue-72312.rs:2:22 | LL | fn require_static(val: T) -> T { | ^^^^^^^ error: aborting due to previous error For more information about this error, try `rustc --explain E0759`. ``` Fix #72312. --- .../src/diagnostics/bound_region_errors.rs | 1 + .../src/infer/error_reporting/mod.rs | 3 +- .../error_reporting/nice_region_error/mod.rs | 2 +- .../nice_region_error/placeholder_error.rs | 4 ++ .../nice_region_error/static_impl_trait.rs | 69 ++++++++++++++++--- .../trait_impl_difference.rs | 1 + .../src/infer/lexical_region_resolve/mod.rs | 32 +++++++-- .../ui/async-await/issues/issue-62097.stderr | 18 +++-- src/test/ui/async-await/issues/issue-72312.rs | 19 +++++ .../ui/async-await/issues/issue-72312.stderr | 20 ++++++ .../trait-upcasting/type-checking-test-4.rs | 18 +++++ .../type-checking-test-4.stderr | 48 ++++++++++++- 12 files changed, 212 insertions(+), 23 deletions(-) create mode 100644 src/test/ui/async-await/issues/issue-72312.rs create mode 100644 src/test/ui/async-await/issues/issue-72312.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 1bc9f8cf3cc..881ebed6029 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -368,6 +368,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>( error_region, cause.clone(), placeholder_region, + vec![], ), ), (Some(error_region), _) => NiceRegionError::new( diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 85226e60bdb..c3f2213229a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -384,6 +384,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sub_r, sup_origin, sup_r, + _, ) => { if sub_r.is_placeholder() { self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit(); @@ -464,7 +465,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { errors.sort_by_key(|u| match *u { RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), }); errors diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index 6a330977002..fd295b74342 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -67,7 +67,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { match (&self.error, self.regions) { (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)), - (Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => { + (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => { Some((origin.span(), sub, sup)) } (None, Some((span, sub, sup))) => Some((span, sub, sup)), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index 4aecc2f40b8..1a4a2803821 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -34,6 +34,7 @@ impl NiceRegionError<'me, 'tcx> { sub_placeholder @ ty::RePlaceholder(_), _, sup_placeholder @ ty::RePlaceholder(_), + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -49,6 +50,7 @@ impl NiceRegionError<'me, 'tcx> { sub_placeholder @ ty::RePlaceholder(_), _, _, + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -64,6 +66,7 @@ impl NiceRegionError<'me, 'tcx> { _, _, sup_placeholder @ ty::RePlaceholder(_), + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -79,6 +82,7 @@ impl NiceRegionError<'me, 'tcx> { _, SubregionOrigin::Subtype(box TypeTrace { cause, values }), sup_placeholder @ ty::RePlaceholder(_), + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 2aaebed28ce..c7ba5087b8c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -23,7 +23,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn try_report_static_impl_trait(&self) -> Option { debug!("try_report_static_impl_trait(error={:?})", self.error); let tcx = self.tcx(); - let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? { + let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? { RegionResolutionError::SubSupConflict( _, var_origin, @@ -31,8 +31,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sub_r, sup_origin, sup_r, + spans, ) if **sub_r == RegionKind::ReStatic => { - (var_origin, sub_origin, sub_r, sup_origin, sup_r) + (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) } RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, .. }), @@ -123,15 +124,31 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { param_name, lifetime, ); - err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); + + let (mention_capture, capture_point) = if sup_origin.span().overlaps(param.param_ty_span) { + // Account for `async fn` like in `async-await/issues/issue-62097.rs`. + // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same + // place (but with different `ctxt`, hence `overlaps` instead of `==` above). + // + // This avoids the following: + // + // LL | pub async fn run_dummy_fn(&self) { + // | ^^^^^ + // | | + // | this data with an anonymous lifetime `'_`... + // | ...is captured here... + (false, sup_origin.span()) + } else { + (true, param.param_ty_span) + }; + err.span_label(capture_point, &format!("this data with {}...", lifetime)); + debug!("try_report_static_impl_trait: param_info={:?}", param); // We try to make the output have fewer overlapping spans if possible. if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span())) && sup_origin.span() != return_sp { - // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs` - // Customize the spans and labels depending on their relative order so // that split sentences flow correctly. if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() { @@ -152,11 +169,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // | ---- ^ err.span_label( sup_origin.span(), - "...is captured here, requiring it to live as long as `'static`", + &format!( + "...is captured here, requiring it to live as long as `'static`{}", + if spans.is_empty() { "" } else { "..." }, + ), ); } else { - err.span_label(sup_origin.span(), "...is captured here..."); - if return_sp < sup_origin.span() { + if return_sp < sup_origin.span() && mention_capture { + err.span_label(sup_origin.span(), "...is captured here..."); err.span_note( return_sp, "...and is required to live as long as `'static` here", @@ -164,17 +184,46 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } else { err.span_label( return_sp, - "...and is required to live as long as `'static` here", + &format!( + "...is required to live as long as `'static` here{}", + if spans.is_empty() { "" } else { "..." }, + ), ); + if mention_capture { + let span = sup_origin.span(); + let msg = if spans.iter().any(|sp| *sp > span) { + "...is captured here..." + } else { + "...and is captured here" + }; + err.span_label(span, msg); + } } } } else { err.span_label( return_sp, - "...is captured and required to live as long as `'static` here", + &format!( + "...is captured and required to live as long as `'static` here{}", + if spans.is_empty() { "" } else { "..." }, + ), ); } + for span in spans { + let msg = + format!("...and is captured here{}", if mention_capture { " too" } else { "" }); + if span.overlaps(return_sp) { + err.span_note(*span, &msg); + } else { + err.span_label(*span, &msg); + } + } + + if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { + err.span_note(*bound, "`'static` lifetime requirement introduced by this trait bound"); + } + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); let mut override_error_code = None; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index cfa79213c80..452ca5eeabd 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { _sub, sup_origin, _sup, + _, ) = error.clone() { if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) { diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 4c9dcab26b1..14f6c72bb1c 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -19,6 +19,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; +use rustc_span::Span; use std::fmt; /// This function performs lexical region resolution given a complete @@ -96,6 +97,7 @@ pub enum RegionResolutionError<'tcx> { Region<'tcx>, SubregionOrigin<'tcx>, Region<'tcx>, + Vec, ), /// Indicates a `'b: 'a` constraint where `'a` is in a universe that @@ -144,8 +146,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let graph = self.construct_graph(); self.expand_givens(&graph); self.expansion(&mut var_data); - self.collect_errors(&mut var_data, errors); - self.collect_var_errors(&var_data, &graph, errors); + let captures = self.collect_errors(&mut var_data, errors); + self.collect_var_errors(&var_data, &graph, errors, captures); var_data } @@ -443,9 +445,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { &self, var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec>, - ) { + ) -> Vec { + let mut captures = vec![]; + for (constraint, origin) in &self.data.constraints { debug!(?constraint, ?origin); + if let (Constraint::VarSubVar(_, _), SubregionOrigin::DataBorrowed(_, sp)) = + (constraint, origin) + { + captures.push(*sp); + } match *constraint { Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { // Expansion will ensure that these constraints hold. Ignore. @@ -515,6 +524,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { sub, )); } + captures } /// Go over the variables that were declared to be error variables @@ -524,6 +534,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { var_data: &LexicalRegionResolutions<'tcx>, graph: &RegionGraph<'tcx>, errors: &mut Vec>, + captures: Vec, ) { debug!("collect_var_errors, var_data = {:#?}", var_data.values); @@ -567,7 +578,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // if this rule starts to create problems we'll // have to revisit this portion of the code and // think hard about it. =) -- nikomatsakis - self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); + self.collect_error_for_expanding_node( + graph, + &mut dup_vec, + node_vid, + errors, + &captures, + ); } } } @@ -621,6 +638,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { dup_vec: &mut IndexVec>, node_idx: RegionVid, errors: &mut Vec>, + captures: &[Span], ) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. @@ -667,6 +685,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { sup: {:?}", origin, node_idx, lower_bound.region, upper_bound.region ); + + let mut capture_spans: Vec = captures.iter().cloned().collect(); + // Below, one span expects `&Span` and the other `&mut Span`, hence the dupes. + capture_spans.sort_by_key(|span| (span.lo(), span.hi())); + capture_spans.dedup_by_key(|span| (span.lo(), span.hi())); errors.push(RegionResolutionError::SubSupConflict( node_idx, origin, @@ -674,6 +697,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { lower_bound.region, upper_bound.origin.clone(), upper_bound.region, + capture_spans, )); return; } diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index 56a28d904b9..bb329a4a0c2 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -2,12 +2,20 @@ error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `' --> $DIR/issue-62097.rs:12:31 | LL | pub async fn run_dummy_fn(&self) { - | ^^^^^ - | | - | this data with an anonymous lifetime `'_`... - | ...is captured here... + | ^^^^^ this data with an anonymous lifetime `'_`... LL | foo(|| self.bar()).await; - | --- ...and is required to live as long as `'static` here + | --- ...is required to live as long as `'static` here... + | +note: ...and is captured here + --> $DIR/issue-62097.rs:13:9 + | +LL | foo(|| self.bar()).await; + | ^^^^^^^^^^^^^^^^^^^^^^^^ +note: `'static` lifetime requirement introduced by this trait bound + --> $DIR/issue-62097.rs:4:19 + | +LL | F: FnOnce() + 'static + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/async-await/issues/issue-72312.rs b/src/test/ui/async-await/issues/issue-72312.rs new file mode 100644 index 00000000000..d33685e02f1 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-72312.rs @@ -0,0 +1,19 @@ +// edition:2018 +fn require_static(val: T) -> T { + //~^ NOTE 'static` lifetime requirement introduced by this trait bound + val +} + +struct Problem; + +impl Problem { + pub async fn start(&self) { //~ ERROR E0759 + //~^ NOTE this data with an anonymous lifetime `'_` + //~| NOTE in this expansion of desugaring of `async` block or function + require_static(async move { //~ NOTE ...is required to live as long as `'static` here + &self; //~ NOTE ...and is captured here + }); + } +} + +fn main() {} diff --git a/src/test/ui/async-await/issues/issue-72312.stderr b/src/test/ui/async-await/issues/issue-72312.stderr new file mode 100644 index 00000000000..ee5ee6f0f93 --- /dev/null +++ b/src/test/ui/async-await/issues/issue-72312.stderr @@ -0,0 +1,20 @@ +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/issue-72312.rs:10:24 + | +LL | pub async fn start(&self) { + | ^^^^^ this data with an anonymous lifetime `'_`... +... +LL | require_static(async move { + | -------------- ...is required to live as long as `'static` here... +LL | &self; + | ----- ...and is captured here + | +note: `'static` lifetime requirement introduced by this trait bound + --> $DIR/issue-72312.rs:2:22 + | +LL | fn require_static(val: T) -> T { + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs index 9b27fd46f7a..95698fd1e1a 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs @@ -29,4 +29,22 @@ fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { y.get_b() // ERROR } +fn test_wrong4<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { + <_ as Bar>::get_b(x) // ERROR + //~^ ERROR `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement +} + +fn test_wrong5<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { + <_ as Bar<'_, '_>>::get_b(x) // ERROR + //~^ ERROR `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement +} + +fn test_wrong6<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { + let y = x as &dyn Bar<'_, '_>; + //~^ ERROR `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + y.get_b(); // ERROR + let z = y; + z.get_b() // ERROR +} + fn main() {} diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr index 4967f3dc2c8..04560ea4e29 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -39,9 +39,53 @@ LL | let y = x as &dyn Bar<'_, '_>; | ...is captured here... LL | LL | y.get_b() // ERROR - | --------- ...and is required to live as long as `'static` here + | --------- ...is required to live as long as `'static` here... + | +note: ...and is captured here too + --> $DIR/type-checking-test-4.rs:29:5 + | +LL | y.get_b() // ERROR + | ^ -error: aborting due to 3 previous errors +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/type-checking-test-4.rs:33:5 + | +LL | fn test_wrong4<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { + | ------------ this data with lifetime `'a`... +LL | <_ as Bar>::get_b(x) // ERROR + | ^^^^^^^^^^^^^^^^^ ...is captured here, requiring it to live as long as `'static` + +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/type-checking-test-4.rs:38:15 + | +LL | fn test_wrong5<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { + | ------------ this data with lifetime `'a`... +LL | <_ as Bar<'_, '_>>::get_b(x) // ERROR + | ----------^^---------------- ...is captured and required to live as long as `'static` here + +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/type-checking-test-4.rs:43:27 + | +LL | fn test_wrong6<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { + | ------------ this data with lifetime `'a`... +LL | let y = x as &dyn Bar<'_, '_>; + | - ^^ + | | + | ...is captured here... +LL | +LL | y.get_b(); // ERROR + | - ...and is captured here too +LL | let z = y; +LL | z.get_b() // ERROR + | --------- ...is required to live as long as `'static` here... + | +note: ...and is captured here too + --> $DIR/type-checking-test-4.rs:47:5 + | +LL | z.get_b() // ERROR + | ^ + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0308, E0759. For more information about an error, try `rustc --explain E0308`.