Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide more information for HRTB lifetime errors involving closures #79588

Merged
merged 1 commit into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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`
Comment on lines +5 to +8
Copy link
Contributor

@oli-obk oli-obk Dec 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we have the information available anywhere, but could emit a diagnostic of this quality for the the required lifetime does not necessarily outlive the anonymous lifetime #1 defined on the body, too? This occurs in src/test/ui/generator/resume-arg-late-bound.stderr


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
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
--> $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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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) {}
| ^^^
Comment on lines +14 to +18
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't considered this case in particular, but ideally we would (also) point at the where clause in impl<F> Foo for F where F: Fn(&i32) -> &i32 for this...


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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn thing(x: impl FnOnce(&u32)) {}

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