-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Greatly improve error reporting for futures and generators in note_obligation_cause_code
#98259
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ use crate::infer::InferCtxt; | |
use crate::traits::normalize_to; | ||
|
||
use hir::HirId; | ||
use rustc_ast::Movability; | ||
use rustc_data_structures::fx::FxHashSet; | ||
use rustc_data_structures::stack::ensure_sufficient_stack; | ||
use rustc_errors::{ | ||
|
@@ -2397,24 +2398,104 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | |
{ | ||
let parent_trait_ref = | ||
self.resolve_vars_if_possible(data.parent_trait_pred); | ||
let ty = parent_trait_ref.skip_binder().self_ty(); | ||
matches!(ty.kind(), ty::Generator(..)) | ||
|| matches!(ty.kind(), ty::Closure(..)) | ||
let nested_ty = parent_trait_ref.skip_binder().self_ty(); | ||
matches!(nested_ty.kind(), ty::Generator(..)) | ||
|| matches!(nested_ty.kind(), ty::Closure(..)) | ||
} else { | ||
false | ||
} | ||
}; | ||
|
||
let future_trait = self.tcx.lang_items().future_trait().unwrap(); | ||
let opaque_ty_is_future = |def_id| { | ||
self.tcx.explicit_item_bounds(def_id).iter().any(|(predicate, _)| { | ||
if let ty::PredicateKind::Trait(trait_predicate) = | ||
predicate.kind().skip_binder() | ||
{ | ||
trait_predicate.trait_ref.def_id == future_trait | ||
} else { | ||
false | ||
} | ||
}) | ||
}; | ||
|
||
let from_generator = tcx.lang_items().from_generator_fn().unwrap(); | ||
|
||
// Don't print the tuple of capture types | ||
if !is_upvar_tys_infer_tuple { | ||
let msg = format!("required because it appears within the type `{}`", ty); | ||
match ty.kind() { | ||
ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { | ||
Some(ident) => err.span_note(ident.span, &msg), | ||
None => err.note(&msg), | ||
}, | ||
_ => err.note(&msg), | ||
}; | ||
'print: { | ||
if !is_upvar_tys_infer_tuple { | ||
let msg = format!("required because it appears within the type `{}`", ty); | ||
match ty.kind() { | ||
ty::Adt(def, _) => { | ||
// `gen_future` is used in all async functions; it doesn't add any additional info. | ||
if self.tcx.is_diagnostic_item(sym::gen_future, def.did()) { | ||
break 'print; | ||
} | ||
match self.tcx.opt_item_ident(def.did()) { | ||
Some(ident) => err.span_note(ident.span, &msg), | ||
None => err.note(&msg), | ||
} | ||
} | ||
ty::Opaque(def_id, _) => { | ||
// Avoid printing the future from `core::future::from_generator`, it's not helpful | ||
if tcx.parent(*def_id) == from_generator { | ||
break 'print; | ||
} | ||
|
||
// If the previous type is `from_generator`, this is the future generated by the body of an async function. | ||
// Avoid printing it twice (it was already printed in the `ty::Generator` arm below). | ||
let is_future = opaque_ty_is_future(def_id); | ||
debug!( | ||
?obligated_types, | ||
?is_future, | ||
"note_obligation_cause_code: check for async fn" | ||
); | ||
if opaque_ty_is_future(def_id) | ||
&& obligated_types.last().map_or(false, |ty| match ty.kind() { | ||
ty::Opaque(last_def_id, _) => { | ||
tcx.parent(*last_def_id) == from_generator | ||
} | ||
_ => false, | ||
}) | ||
{ | ||
break 'print; | ||
} | ||
err.span_note(self.tcx.def_span(def_id), &msg) | ||
} | ||
ty::GeneratorWitness(bound_tys) => { | ||
use std::fmt::Write; | ||
|
||
// FIXME: this is kind of an unusual format for rustc, can we make it more clear? | ||
// Maybe we should just remove this note altogether? | ||
// FIXME: only print types which don't meet the trait requirement | ||
let mut msg = | ||
"required because it captures the following types: ".to_owned(); | ||
for ty in bound_tys.skip_binder() { | ||
write!(msg, "`{}`, ", ty).unwrap(); | ||
} | ||
err.note(msg.trim_end_matches(", ")) | ||
} | ||
ty::Generator(def_id, _, movability) => { | ||
let sp = self.tcx.def_span(def_id); | ||
|
||
// Special-case this to say "async block" instead of `[static generator]`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this always true that a static generator is an async block? I guess it probably is for current stable Rust. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh good point - I'll change this to use https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.generator_kind instead :) |
||
let kind = if *movability == Movability::Static { | ||
"async block" | ||
} else { | ||
"generator" | ||
}; | ||
err.span_note( | ||
sp, | ||
&format!("required because it's used within this {}", kind), | ||
) | ||
} | ||
ty::Closure(def_id, _) => err.span_note( | ||
self.tcx.def_span(def_id), | ||
&format!("required because it's used within this closure"), | ||
), | ||
_ => err.note(&msg), | ||
}; | ||
} | ||
} | ||
|
||
obligated_types.push(ty); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
error[E0277]: `Sender<i32>` cannot be shared between threads safely | ||
--> $DIR/issue-70935-complex-spans.rs:13:45 | ||
| | ||
LL | fn foo(tx: std::sync::mpsc::Sender<i32>) -> impl Future + Send { | ||
| ^^^^^^^^^^^^^^^^^^ `Sender<i32>` cannot be shared between threads safely | ||
| | ||
= help: the trait `Sync` is not implemented for `Sender<i32>` | ||
= note: required because of the requirements on the impl of `Send` for `&Sender<i32>` | ||
note: required because it's used within this closure | ||
--> $DIR/issue-70935-complex-spans.rs:25:13 | ||
| | ||
LL | baz(|| async{ | ||
| _____________^ | ||
LL | | foo(tx.clone()); | ||
LL | | }).await; | ||
| |_________^ | ||
note: required because it's used within this async block | ||
--> $DIR/issue-70935-complex-spans.rs:9:67 | ||
| | ||
LL | async fn baz<T>(_c: impl FnMut() -> T) where T: Future<Output=()> { | ||
| ___________________________________________________________________^ | ||
LL | | | ||
LL | | } | ||
| |_^ | ||
= note: required because it captures the following types: `ResumeTy`, `impl Future<Output = ()>`, `()` | ||
note: required because it's used within this async block | ||
--> $DIR/issue-70935-complex-spans.rs:23:16 | ||
| | ||
LL | async move { | ||
| ________________^ | ||
LL | | | ||
LL | | baz(|| async{ | ||
LL | | foo(tx.clone()); | ||
LL | | }).await; | ||
LL | | } | ||
| |_____^ | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0277`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it's worth lifting this out into a method on
tcx
? It seems like code that might be useful elsewhere too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, done :)