diff options
| author | Michael Goulet <michael@errs.io> | 2022-06-23 14:39:06 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-23 14:39:06 -0700 |
| commit | 413e350f87fafeea3da13fc98337c632c4cfcd70 (patch) | |
| tree | a94144d354069b6619cf4dc4c3f099f2f3db01e9 /compiler | |
| parent | 49bcc7057457f758d18cbd22aa2b8a251ee6510c (diff) | |
| parent | 1deca0425db3e74a61cb732e729c0777904e549c (diff) | |
| download | rust-413e350f87fafeea3da13fc98337c632c4cfcd70.tar.gz rust-413e350f87fafeea3da13fc98337c632c4cfcd70.zip | |
Rollup merge of #98259 - jyn514:improve-obligation-errors, r=estebank
Greatly improve error reporting for futures and generators in `note_obligation_cause_code` Most futures don't go through this code path, because they're caught by `maybe_note_obligation_cause_for_async_await`. But all generators do, and `maybe_note` is imperfect and doesn't catch all futures. Improve the error message for those it misses. At some point, we may want to consider unifying this with the code for `maybe_note_async_await`, so that `async_await` notes all parent constraints, and `note_obligation` can point to yield points. But both functions are quite complicated, and it's not clear to me how to combine them; this seems like a good incremental improvement. Helps with https://github.com/rust-lang/rust/issues/97332. r? ``@estebank`` cc ``@eholk`` ``@compiler-errors``
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs | 105 |
1 files changed, 93 insertions, 12 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 0485cac9e9f..e699577b71c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -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::{ @@ -2386,24 +2387,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]`. + 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); |
