summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs501
1 files changed, 501 insertions, 0 deletions
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
new file mode 100644
index 000000000..998699158
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -0,0 +1,501 @@
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::ValuePairs;
+use crate::infer::{SubregionOrigin, TypeTrace};
+use crate::traits::{ObligationCause, ObligationCauseCode};
+use rustc_data_structures::intern::Interned;
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_hir::def::Namespace;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode};
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt};
+
+use std::fmt::{self, Write};
+
+impl<'tcx> NiceRegionError<'_, 'tcx> {
+ /// When given a `ConcreteFailure` for a function with arguments containing a named region and
+ /// an anonymous region, emit a descriptive diagnostic error.
+ pub(super) fn try_report_placeholder_conflict(
+ &self,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+ match &self.error {
+ ///////////////////////////////////////////////////////////////////////////
+ // NB. The ordering of cases in this match is very
+ // sensitive, because we are often matching against
+ // specific cases and then using an `_` to match all
+ // others.
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Check for errors from comparing trait failures -- first
+ // with two placeholders, then with one.
+ Some(RegionResolutionError::SubSupConflict(
+ vid,
+ _,
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
+ _,
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+ _,
+ )) => self.try_report_trait_placeholder_mismatch(
+ Some(self.tcx().mk_region(ReVar(*vid))),
+ cause,
+ Some(*sub_placeholder),
+ Some(*sup_placeholder),
+ values,
+ ),
+
+ Some(RegionResolutionError::SubSupConflict(
+ vid,
+ _,
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
+ _,
+ _,
+ _,
+ )) => self.try_report_trait_placeholder_mismatch(
+ Some(self.tcx().mk_region(ReVar(*vid))),
+ cause,
+ Some(*sub_placeholder),
+ None,
+ values,
+ ),
+
+ Some(RegionResolutionError::SubSupConflict(
+ vid,
+ _,
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ _,
+ _,
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+ _,
+ )) => self.try_report_trait_placeholder_mismatch(
+ Some(self.tcx().mk_region(ReVar(*vid))),
+ cause,
+ None,
+ Some(*sup_placeholder),
+ values,
+ ),
+
+ Some(RegionResolutionError::SubSupConflict(
+ vid,
+ _,
+ _,
+ _,
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+ _,
+ )) => self.try_report_trait_placeholder_mismatch(
+ Some(self.tcx().mk_region(ReVar(*vid))),
+ cause,
+ None,
+ Some(*sup_placeholder),
+ values,
+ ),
+
+ Some(RegionResolutionError::UpperBoundUniverseConflict(
+ vid,
+ _,
+ _,
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+ )) => self.try_report_trait_placeholder_mismatch(
+ Some(self.tcx().mk_region(ReVar(*vid))),
+ cause,
+ None,
+ Some(*sup_placeholder),
+ values,
+ ),
+
+ Some(RegionResolutionError::ConcreteFailure(
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sub_region @ Region(Interned(RePlaceholder(_), _)),
+ sup_region @ Region(Interned(RePlaceholder(_), _)),
+ )) => self.try_report_trait_placeholder_mismatch(
+ None,
+ cause,
+ Some(*sub_region),
+ Some(*sup_region),
+ values,
+ ),
+
+ Some(RegionResolutionError::ConcreteFailure(
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sub_region @ Region(Interned(RePlaceholder(_), _)),
+ sup_region,
+ )) => self.try_report_trait_placeholder_mismatch(
+ (!sup_region.has_name()).then_some(*sup_region),
+ cause,
+ Some(*sub_region),
+ None,
+ values,
+ ),
+
+ Some(RegionResolutionError::ConcreteFailure(
+ SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+ sub_region,
+ sup_region @ Region(Interned(RePlaceholder(_), _)),
+ )) => self.try_report_trait_placeholder_mismatch(
+ (!sub_region.has_name()).then_some(*sub_region),
+ cause,
+ None,
+ Some(*sup_region),
+ values,
+ ),
+
+ _ => None,
+ }
+ }
+
+ fn try_report_trait_placeholder_mismatch(
+ &self,
+ vid: Option<Region<'tcx>>,
+ cause: &ObligationCause<'tcx>,
+ sub_placeholder: Option<Region<'tcx>>,
+ sup_placeholder: Option<Region<'tcx>>,
+ value_pairs: &ValuePairs<'tcx>,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+ let (expected_substs, found_substs, trait_def_id) = match value_pairs {
+ ValuePairs::TraitRefs(ExpectedFound { expected, found })
+ if expected.def_id == found.def_id =>
+ {
+ (expected.substs, found.substs, expected.def_id)
+ }
+ ValuePairs::PolyTraitRefs(ExpectedFound { expected, found })
+ if expected.def_id() == found.def_id() =>
+ {
+ // It's possible that the placeholders come from a binder
+ // outside of this value pair. Use `no_bound_vars` as a
+ // simple heuristic for that.
+ (expected.no_bound_vars()?.substs, found.no_bound_vars()?.substs, expected.def_id())
+ }
+ _ => return None,
+ };
+
+ Some(self.report_trait_placeholder_mismatch(
+ vid,
+ cause,
+ sub_placeholder,
+ sup_placeholder,
+ trait_def_id,
+ expected_substs,
+ found_substs,
+ ))
+ }
+
+ // error[E0308]: implementation of `Foo` does not apply to enough lifetimes
+ // --> /home/nmatsakis/tmp/foo.rs:12:5
+ // |
+ // 12 | all::<&'static u32>();
+ // | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
+ // |
+ // = note: Due to a where-clause on the function `all`,
+ // = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
+ // = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
+ #[instrument(level = "debug", skip(self))]
+ fn report_trait_placeholder_mismatch(
+ &self,
+ vid: Option<Region<'tcx>>,
+ cause: &ObligationCause<'tcx>,
+ sub_placeholder: Option<Region<'tcx>>,
+ sup_placeholder: Option<Region<'tcx>>,
+ trait_def_id: DefId,
+ expected_substs: SubstsRef<'tcx>,
+ actual_substs: SubstsRef<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let span = cause.span();
+ let msg = format!(
+ "implementation of `{}` is not general enough",
+ self.tcx().def_path_str(trait_def_id),
+ );
+ let mut err = self.tcx().sess.struct_span_err(span, &msg);
+
+ let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = *cause.code() {
+ err.span_label(span, "doesn't satisfy where-clause");
+ err.span_label(
+ self.tcx().def_span(def_id),
+ &format!("due to a where-clause on `{}`...", self.tcx().def_path_str(def_id)),
+ );
+ true
+ } else {
+ err.span_label(span, &msg);
+ false
+ };
+
+ let expected_trait_ref = self.infcx.resolve_vars_if_possible(ty::TraitRef {
+ def_id: trait_def_id,
+ substs: expected_substs,
+ });
+ let actual_trait_ref = self
+ .infcx
+ .resolve_vars_if_possible(ty::TraitRef { def_id: trait_def_id, substs: actual_substs });
+
+ // Search the expected and actual trait references to see (a)
+ // whether the sub/sup placeholders appear in them (sometimes
+ // you have a trait ref like `T: Foo<fn(&u8)>`, where the
+ // placeholder was created as part of an inner type) and (b)
+ // whether the inference variable appears. In each case,
+ // assign a counter value in each case if so.
+ let mut counter = 0;
+ let mut has_sub = None;
+ let mut has_sup = None;
+
+ let mut actual_has_vid = None;
+ let mut expected_has_vid = None;
+
+ self.tcx().for_each_free_region(&expected_trait_ref, |r| {
+ if Some(r) == sub_placeholder && has_sub.is_none() {
+ has_sub = Some(counter);
+ counter += 1;
+ } else if Some(r) == sup_placeholder && has_sup.is_none() {
+ has_sup = Some(counter);
+ counter += 1;
+ }
+
+ if Some(r) == vid && expected_has_vid.is_none() {
+ expected_has_vid = Some(counter);
+ counter += 1;
+ }
+ });
+
+ self.tcx().for_each_free_region(&actual_trait_ref, |r| {
+ if Some(r) == vid && actual_has_vid.is_none() {
+ actual_has_vid = Some(counter);
+ counter += 1;
+ }
+ });
+
+ let actual_self_ty_has_vid =
+ self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
+
+ let expected_self_ty_has_vid =
+ self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
+
+ let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
+
+ debug!(
+ ?actual_has_vid,
+ ?expected_has_vid,
+ ?has_sub,
+ ?has_sup,
+ ?actual_self_ty_has_vid,
+ ?expected_self_ty_has_vid,
+ );
+
+ self.explain_actual_impl_that_was_found(
+ &mut err,
+ sub_placeholder,
+ sup_placeholder,
+ has_sub,
+ has_sup,
+ expected_trait_ref,
+ actual_trait_ref,
+ vid,
+ expected_has_vid,
+ actual_has_vid,
+ any_self_ty_has_vid,
+ leading_ellipsis,
+ );
+
+ err
+ }
+
+ /// Add notes with details about the expected and actual trait refs, with attention to cases
+ /// when placeholder regions are involved: either the trait or the self type containing
+ /// them needs to be mentioned the closest to the placeholders.
+ /// This makes the error messages read better, however at the cost of some complexity
+ /// due to the number of combinations we have to deal with.
+ fn explain_actual_impl_that_was_found(
+ &self,
+ err: &mut Diagnostic,
+ sub_placeholder: Option<Region<'tcx>>,
+ sup_placeholder: Option<Region<'tcx>>,
+ has_sub: Option<usize>,
+ has_sup: Option<usize>,
+ expected_trait_ref: ty::TraitRef<'tcx>,
+ actual_trait_ref: ty::TraitRef<'tcx>,
+ vid: Option<Region<'tcx>>,
+ expected_has_vid: Option<usize>,
+ actual_has_vid: Option<usize>,
+ any_self_ty_has_vid: bool,
+ leading_ellipsis: bool,
+ ) {
+ // HACK(eddyb) maybe move this in a more central location.
+ #[derive(Copy, Clone)]
+ struct Highlighted<'tcx, T> {
+ tcx: TyCtxt<'tcx>,
+ highlight: RegionHighlightMode<'tcx>,
+ value: T,
+ }
+
+ impl<'tcx, T> Highlighted<'tcx, T> {
+ fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
+ Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
+ }
+ }
+
+ impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
+ where
+ T: for<'a> Print<
+ 'tcx,
+ FmtPrinter<'a, 'tcx>,
+ Error = fmt::Error,
+ Output = FmtPrinter<'a, 'tcx>,
+ >,
+ {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
+ printer.region_highlight_mode = self.highlight;
+
+ let s = self.value.print(printer)?.into_buffer();
+ f.write_str(&s)
+ }
+ }
+
+ // The weird thing here with the `maybe_highlighting_region` calls and the
+ // the match inside is meant to be like this:
+ //
+ // - The match checks whether the given things (placeholders, etc) appear
+ // in the types are about to print
+ // - Meanwhile, the `maybe_highlighting_region` calls set up
+ // highlights so that, if they do appear, we will replace
+ // them `'0` and whatever. (This replacement takes place
+ // inside the closure given to `maybe_highlighting_region`.)
+ //
+ // There is some duplication between the calls -- i.e., the
+ // `maybe_highlighting_region` checks if (e.g.) `has_sub` is
+ // None, an then we check again inside the closure, but this
+ // setup sort of minimized the number of calls and so form.
+
+ let highlight_trait_ref = |trait_ref| Highlighted {
+ tcx: self.tcx(),
+ highlight: RegionHighlightMode::new(self.tcx()),
+ value: trait_ref,
+ };
+
+ let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
+
+ let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
+ expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
+ expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
+ err.note(&{
+ let passive_voice = match (has_sub, has_sup) {
+ (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
+ (None, None) => {
+ expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
+ match expected_has_vid {
+ Some(_) => true,
+ None => any_self_ty_has_vid,
+ }
+ }
+ };
+
+ let mut note = if same_self_type {
+ let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
+ self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
+
+ if self_ty.value.is_closure()
+ && self
+ .tcx()
+ .fn_trait_kind_from_lang_item(expected_trait_ref.value.def_id)
+ .is_some()
+ {
+ let closure_sig = self_ty.map(|closure| {
+ if let ty::Closure(_, substs) = closure.kind() {
+ self.tcx().signature_unclosure(
+ substs.as_closure().sig(),
+ rustc_hir::Unsafety::Normal,
+ )
+ } else {
+ bug!("type is not longer closure");
+ }
+ });
+
+ format!(
+ "{}closure with signature `{}` must implement `{}`",
+ if leading_ellipsis { "..." } else { "" },
+ closure_sig,
+ expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+ )
+ } else {
+ format!(
+ "{}`{}` must implement `{}`",
+ if leading_ellipsis { "..." } else { "" },
+ self_ty,
+ expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+ )
+ }
+ } else if passive_voice {
+ format!(
+ "{}`{}` would have to be implemented for the type `{}`",
+ if leading_ellipsis { "..." } else { "" },
+ expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+ expected_trait_ref.map(|tr| tr.self_ty()),
+ )
+ } else {
+ format!(
+ "{}`{}` must implement `{}`",
+ if leading_ellipsis { "..." } else { "" },
+ expected_trait_ref.map(|tr| tr.self_ty()),
+ expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+ )
+ };
+
+ match (has_sub, has_sup) {
+ (Some(n1), Some(n2)) => {
+ let _ = write!(
+ note,
+ ", for any two lifetimes `'{}` and `'{}`...",
+ std::cmp::min(n1, n2),
+ std::cmp::max(n1, n2),
+ );
+ }
+ (Some(n), _) | (_, Some(n)) => {
+ let _ = write!(note, ", for any lifetime `'{}`...", n,);
+ }
+ (None, None) => {
+ if let Some(n) = expected_has_vid {
+ let _ = write!(note, ", for some specific lifetime `'{}`...", n,);
+ }
+ }
+ }
+
+ note
+ });
+
+ let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
+ actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
+ err.note(&{
+ let passive_voice = match actual_has_vid {
+ Some(_) => any_self_ty_has_vid,
+ None => true,
+ };
+
+ let mut note = if same_self_type {
+ format!(
+ "...but it actually implements `{}`",
+ actual_trait_ref.map(|tr| tr.print_only_trait_path()),
+ )
+ } else if passive_voice {
+ format!(
+ "...but `{}` is actually implemented for the type `{}`",
+ actual_trait_ref.map(|tr| tr.print_only_trait_path()),
+ actual_trait_ref.map(|tr| tr.self_ty()),
+ )
+ } else {
+ format!(
+ "...but `{}` actually implements `{}`",
+ actual_trait_ref.map(|tr| tr.self_ty()),
+ actual_trait_ref.map(|tr| tr.print_only_trait_path()),
+ )
+ };
+
+ if let Some(n) = actual_has_vid {
+ let _ = write!(note, ", for some specific lifetime `'{}`", n);
+ }
+
+ note
+ });
+ }
+}