Skip to content

Commit

Permalink
Provide more information for HRTB lifetime errors involving closures
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Jan 12, 2021
1 parent 704e47f commit a8a9742
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 25 deletions.
12 changes: 11 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/mod.rs
Expand Up @@ -98,7 +98,7 @@ pub(super) fn note_and_explain_region(
// uh oh, hope no user ever sees THIS
ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None),

ty::RePlaceholder(_) => ("any other region".to_string(), None),
ty::RePlaceholder(_) => return,

// FIXME(#13998) RePlaceholder should probably print like
// ReFree rather than dumping Debug output on the user.
Expand Down Expand Up @@ -1675,6 +1675,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.check_and_note_conflicting_crates(diag, terr);
self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id());

if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values {
if let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() {
if let Some(def_id) = def_id.as_local() {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let span = self.tcx.hir().span(hir_id);
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
}
}
}

// It reads better to have the error origin as the final
// thing.
self.note_error_origin(diag, cause, exp_found);
Expand Down
64 changes: 53 additions & 11 deletions compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -1,6 +1,7 @@
use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
use crate::infer::{self, InferCtxt, SubregionOrigin};
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{self, Region};

Expand Down Expand Up @@ -107,14 +108,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
infer::Subtype(box trace) => {
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
let mut err = self.report_and_explain_type_error(trace, &terr);
note_and_explain_region(self.tcx, &mut err, "", sup, "...");
note_and_explain_region(
self.tcx,
&mut err,
"...does not necessarily outlive ",
sub,
"",
);
match (sub, sup) {
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
(ty::RePlaceholder(_), _) => {
note_and_explain_region(
self.tcx,
&mut err,
"",
sup,
" doesn't meet the lifetime requirements",
);
}
(_, ty::RePlaceholder(_)) => {
note_and_explain_region(
self.tcx,
&mut err,
"the required lifetime does not necessarily outlive ",
sub,
"",
);
}
_ => {
note_and_explain_region(self.tcx, &mut err, "", sup, "...");
note_and_explain_region(
self.tcx,
&mut err,
"...does not necessarily outlive ",
sub,
"",
);
}
}
err
}
infer::Reborrow(span) => {
Expand Down Expand Up @@ -286,13 +310,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
sup: Region<'tcx>,
) -> DiagnosticBuilder<'tcx> {
// I can't think how to do better than this right now. -nikomatsakis
debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
match placeholder_origin {
infer::Subtype(box ref trace)
if matches!(
&trace.cause.code.peel_derives(),
ObligationCauseCode::BindingObligation(..)
) =>
{
// Hack to get around the borrow checker because trace.cause has an `Rc`.
if let ObligationCauseCode::BindingObligation(_, span) =
&trace.cause.code.peel_derives()
{
let span = *span;
let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
err.span_note(span, "the lifetime requirement is introduced here");
err
} else {
unreachable!()
}
}
infer::Subtype(box trace) => {
let terr = TypeError::RegionsPlaceholderMismatch;
self.report_and_explain_type_error(trace, &terr)
return self.report_and_explain_type_error(trace, &terr);
}

_ => self.report_concrete_failure(placeholder_origin, sub, sup),
_ => return self.report_concrete_failure(placeholder_origin, sub, sup),
}
}
}
Expand Up @@ -2,10 +2,15 @@ error[E0308]: mismatched types
--> $DIR/higher-ranked-projection.rs:25:5
|
LL | foo(());
| ^^^ one type is more general than the other
| ^^^ lifetime mismatch
|
= note: expected type `&'a ()`
found type `&()`
note: the lifetime requirement is introduced here
--> $DIR/higher-ranked-projection.rs:15:33
|
LL | where for<'a> &'a T: Mirror<Image=U>
| ^^^^^^^

error: aborting due to previous error

Expand Down
32 changes: 30 additions & 2 deletions src/test/ui/generator/resume-arg-late-bound.stderr
Expand Up @@ -2,19 +2,47 @@ error[E0308]: mismatched types
--> $DIR/resume-arg-late-bound.rs:15:5
|
LL | test(gen);
| ^^^^ one type is more general than the other
| ^^^^ lifetime mismatch
|
= note: expected type `for<'a> Generator<&'a mut bool>`
found type `Generator<&mut bool>`
note: the required lifetime does not necessarily outlive the anonymous lifetime #1 defined on the body at 11:15
--> $DIR/resume-arg-late-bound.rs:11:15
|
LL | let gen = |arg: &mut bool| {
| _______________^
LL | | yield ();
LL | | *arg = true;
LL | | };
| |_____^
note: the lifetime requirement is introduced here
--> $DIR/resume-arg-late-bound.rs:8:17
|
LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/resume-arg-late-bound.rs:15:5
|
LL | test(gen);
| ^^^^ one type is more general than the other
| ^^^^ lifetime mismatch
|
= note: expected type `for<'a> Generator<&'a mut bool>`
found type `Generator<&mut bool>`
note: the anonymous lifetime #1 defined on the body at 11:15 doesn't meet the lifetime requirements
--> $DIR/resume-arg-late-bound.rs:11:15
|
LL | let gen = |arg: &mut bool| {
| _______________^
LL | | yield ();
LL | | *arg = true;
LL | | };
| |_____^
note: the lifetime requirement is introduced here
--> $DIR/resume-arg-late-bound.rs:8:17
|
LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
24 changes: 22 additions & 2 deletions src/test/ui/hrtb/hrtb-perfect-forwarding.stderr
Expand Up @@ -2,19 +2,39 @@ error[E0308]: mismatched types
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^ one type is more general than the other
| ^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `Bar<&'a isize>`
found type `Bar<&'b isize>`
note: the required lifetime does not necessarily outlive the lifetime `'b` as defined on the function body at 39:21
--> $DIR/hrtb-perfect-forwarding.rs:39:21
|
LL | fn foo_hrtb_bar_not<'b,T>(mut t: T)
| ^^
note: the lifetime requirement is introduced here
--> $DIR/hrtb-perfect-forwarding.rs:40:15
|
LL | where T : for<'a> Foo<&'a isize> + Bar<&'b isize>
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^ one type is more general than the other
| ^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `Bar<&'a isize>`
found type `Bar<&'b isize>`
note: the lifetime `'b` as defined on the function body at 39:21 doesn't meet the lifetime requirements
--> $DIR/hrtb-perfect-forwarding.rs:39:21
|
LL | fn foo_hrtb_bar_not<'b,T>(mut t: T)
| ^^
note: the lifetime requirement is introduced here
--> $DIR/hrtb-perfect-forwarding.rs:40:15
|
LL | where T : for<'a> Foo<&'a isize> + Bar<&'b isize>
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
2 changes: 0 additions & 2 deletions src/test/ui/issues/issue-26217.stderr
Expand Up @@ -3,8 +3,6 @@ error[E0477]: the type `&'a i32` does not fulfill the required lifetime
|
LL | foo::<&'a i32>();
| ^^^^^^^^^^^^^^
|
= note: type must outlive any other region

error: aborting due to previous error

Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/issues/issue-57843.stderr
Expand Up @@ -6,6 +6,11 @@ LL | Foo(Box::new(|_| ()));
|
= note: expected type `FnOnce<(&'a bool,)>`
found type `FnOnce<(&bool,)>`
note: this closure does not fulfill the lifetime requirements
--> $DIR/issue-57843.rs:23:18
|
LL | Foo(Box::new(|_| ()));
| ^^^^^^

error: aborting due to previous error

Expand Down
44 changes: 44 additions & 0 deletions src/test/ui/lifetimes/issue-79187-2.nll.stderr
@@ -0,0 +1,44 @@
error: lifetime may not live long enough
--> $DIR/issue-79187-2.rs:9:24
|
LL | take_foo(|a: &i32| a);
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i32
| let's call the lifetime of this reference `'1`

error: lifetime may not live long enough
--> $DIR/issue-79187-2.rs:10:34
|
LL | take_foo(|a: &i32| -> &i32 { a });
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | let's call the lifetime of this reference `'2`
| let's call the lifetime of this reference `'1`

error: higher-ranked subtype error
--> $DIR/issue-79187-2.rs:8:5
|
LL | take_foo(|a| a);
| ^^^^^^^^^^^^^^^

error: higher-ranked subtype error
--> $DIR/issue-79187-2.rs:8:5
|
LL | take_foo(|a| a);
| ^^^^^^^^^^^^^^^

error: higher-ranked subtype error
--> $DIR/issue-79187-2.rs:9:5
|
LL | take_foo(|a: &i32| a);
| ^^^^^^^^^^^^^^^^^^^^^

error: higher-ranked subtype error
--> $DIR/issue-79187-2.rs:10:5
|
LL | take_foo(|a: &i32| -> &i32 { a });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 6 previous errors

23 changes: 23 additions & 0 deletions src/test/ui/lifetimes/issue-79187-2.rs
@@ -0,0 +1,23 @@
trait Foo {}

impl<F> Foo for F where F: Fn(&i32) -> &i32 {}

fn take_foo(_: impl Foo) {}

fn main() {
take_foo(|a| a); //~ ERROR mismatched types
take_foo(|a: &i32| a); //~ ERROR mismatched types
take_foo(|a: &i32| -> &i32 { a }); //~ ERROR mismatched types

// OK
take_foo(identity(|a| a));
take_foo(identity(|a: &i32| a));
take_foo(identity(|a: &i32| -> &i32 { a }));

fn identity<F>(t: F) -> F
where
F: Fn(&i32) -> &i32,
{
t
}
}
60 changes: 60 additions & 0 deletions src/test/ui/lifetimes/issue-79187-2.stderr
@@ -0,0 +1,60 @@
error[E0308]: mismatched types
--> $DIR/issue-79187-2.rs:8:5
|
LL | take_foo(|a| a);
| ^^^^^^^^ lifetime mismatch
|
= note: expected type `for<'r> Fn<(&'r i32,)>`
found type `Fn<(&i32,)>`
note: this closure does not fulfill the lifetime requirements
--> $DIR/issue-79187-2.rs:8:14
|
LL | take_foo(|a| a);
| ^^^^^
note: the lifetime requirement is introduced here
--> $DIR/issue-79187-2.rs:5:21
|
LL | fn take_foo(_: impl Foo) {}
| ^^^

error[E0308]: mismatched types
--> $DIR/issue-79187-2.rs:9:5
|
LL | take_foo(|a: &i32| a);
| ^^^^^^^^ lifetime mismatch
|
= note: expected reference `&i32`
found reference `&i32`
note: the anonymous lifetime #1 defined on the body at 9:14 doesn't meet the lifetime requirements
--> $DIR/issue-79187-2.rs:9:14
|
LL | take_foo(|a: &i32| a);
| ^^^^^^^^^^^
note: the lifetime requirement is introduced here
--> $DIR/issue-79187-2.rs:5:21
|
LL | fn take_foo(_: impl Foo) {}
| ^^^

error[E0308]: mismatched types
--> $DIR/issue-79187-2.rs:10:5
|
LL | take_foo(|a: &i32| -> &i32 { a });
| ^^^^^^^^ lifetime mismatch
|
= note: expected reference `&i32`
found reference `&i32`
note: the anonymous lifetime #1 defined on the body at 10:14 doesn't meet the lifetime requirements
--> $DIR/issue-79187-2.rs:10:14
|
LL | take_foo(|a: &i32| -> &i32 { a });
| ^^^^^^^^^^^^^^^^^^^^^^^
note: the lifetime requirement is introduced here
--> $DIR/issue-79187-2.rs:5:21
|
LL | fn take_foo(_: impl Foo) {}
| ^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0308`.
14 changes: 14 additions & 0 deletions src/test/ui/lifetimes/issue-79187.nll.stderr
@@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/issue-79187.rs:5:5
|
LL | thing(f);
| ^^^^^^^^

error: higher-ranked subtype error
--> $DIR/issue-79187.rs:5:5
|
LL | thing(f);
| ^^^^^^^^

error: aborting due to 2 previous errors

6 changes: 6 additions & 0 deletions src/test/ui/lifetimes/issue-79187.rs
@@ -0,0 +1,6 @@
fn thing(x: impl FnOnce(&u32)) {}

fn main() {
let f = |_| ();
thing(f); //~ERROR mismatched types
}

0 comments on commit a8a9742

Please sign in to comment.