compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs RUST 497 lines View on github.com → Search inside
1use std::fmt;23use rustc_data_structures::intern::Interned;4use rustc_errors::{Diag, IntoDiagArg};5use rustc_hir::def::Namespace;6use rustc_hir::def_id::{CRATE_DEF_ID, DefId};7use rustc_middle::bug;8use rustc_middle::ty::error::ExpectedFound;9use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};10use rustc_middle::ty::{self, GenericArgsRef, RePlaceholder, Region, TyCtxt};11use tracing::{debug, instrument};1213use crate::error_reporting::infer::nice_region_error::NiceRegionError;14use crate::errors::{15    ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes,16    TraitPlaceholderMismatch, TyOrSig,17};18use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace, ValuePairs};19use crate::traits::{ObligationCause, ObligationCauseCode};2021#[derive(Copy, Clone)]22pub(crate) struct Highlighted<'tcx, T> {23    pub tcx: TyCtxt<'tcx>,24    pub highlight: RegionHighlightMode<'tcx>,25    pub value: T,26    pub ns: Namespace,27}2829impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>30where31    T: for<'a> Print<FmtPrinter<'a, 'tcx>>,32{33    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {34        rustc_errors::DiagArgValue::Str(self.to_string().into())35    }36}3738impl<'tcx, T> Highlighted<'tcx, T> {39    fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {40        Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value), ns: self.ns }41    }42}4344impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>45where46    T: for<'a> Print<FmtPrinter<'a, 'tcx>>,47{48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {49        let mut p = ty::print::FmtPrinter::new(self.tcx, self.ns);50        p.region_highlight_mode = self.highlight;5152        self.value.print(&mut p)?;53        f.write_str(&p.into_buffer())54    }55}5657impl<'tcx> NiceRegionError<'_, 'tcx> {58    /// When given a `ConcreteFailure` for a function with arguments containing a named region and59    /// an anonymous region, emit a descriptive diagnostic error.60    pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> {61        match &self.error {62            ///////////////////////////////////////////////////////////////////////////63            // NB. The ordering of cases in this match is very64            // sensitive, because we are often matching against65            // specific cases and then using an `_` to match all66            // others.6768            ///////////////////////////////////////////////////////////////////////////69            // Check for errors from comparing trait failures -- first70            // with two placeholders, then with one.71            Some(RegionResolutionError::SubSupConflict(72                vid,73                _,74                SubregionOrigin::Subtype(box TypeTrace { cause, values }),75                sub_placeholder @ Region(Interned(RePlaceholder(_), _)),76                _,77                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),78                _,79            )) => self.try_report_trait_placeholder_mismatch(80                Some(ty::Region::new_var(self.tcx(), *vid)),81                cause,82                Some(*sub_placeholder),83                Some(*sup_placeholder),84                values,85            ),8687            Some(RegionResolutionError::SubSupConflict(88                vid,89                _,90                SubregionOrigin::Subtype(box TypeTrace { cause, values }),91                sub_placeholder @ Region(Interned(RePlaceholder(_), _)),92                _,93                _,94                _,95            )) => self.try_report_trait_placeholder_mismatch(96                Some(ty::Region::new_var(self.tcx(), *vid)),97                cause,98                Some(*sub_placeholder),99                None,100                values,101            ),102103            Some(RegionResolutionError::SubSupConflict(104                vid,105                _,106                SubregionOrigin::Subtype(box TypeTrace { cause, values }),107                _,108                _,109                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),110                _,111            )) => self.try_report_trait_placeholder_mismatch(112                Some(ty::Region::new_var(self.tcx(), *vid)),113                cause,114                None,115                Some(*sup_placeholder),116                values,117            ),118119            Some(RegionResolutionError::SubSupConflict(120                vid,121                _,122                _,123                _,124                SubregionOrigin::Subtype(box TypeTrace { cause, values }),125                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),126                _,127            )) => self.try_report_trait_placeholder_mismatch(128                Some(ty::Region::new_var(self.tcx(), *vid)),129                cause,130                None,131                Some(*sup_placeholder),132                values,133            ),134135            Some(RegionResolutionError::UpperBoundUniverseConflict(136                vid,137                _,138                _,139                SubregionOrigin::Subtype(box TypeTrace { cause, values }),140                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),141            )) => self.try_report_trait_placeholder_mismatch(142                Some(ty::Region::new_var(self.tcx(), *vid)),143                cause,144                None,145                Some(*sup_placeholder),146                values,147            ),148149            Some(RegionResolutionError::ConcreteFailure(150                SubregionOrigin::Subtype(box TypeTrace { cause, values }),151                sub_region @ Region(Interned(RePlaceholder(_), _)),152                sup_region @ Region(Interned(RePlaceholder(_), _)),153            )) => self.try_report_trait_placeholder_mismatch(154                None,155                cause,156                Some(*sub_region),157                Some(*sup_region),158                values,159            ),160161            Some(RegionResolutionError::ConcreteFailure(162                SubregionOrigin::Subtype(box TypeTrace { cause, values }),163                sub_region @ Region(Interned(RePlaceholder(_), _)),164                sup_region,165            )) => self.try_report_trait_placeholder_mismatch(166                (!sup_region.is_named(self.tcx())).then_some(*sup_region),167                cause,168                Some(*sub_region),169                None,170                values,171            ),172173            Some(RegionResolutionError::ConcreteFailure(174                SubregionOrigin::Subtype(box TypeTrace { cause, values }),175                sub_region,176                sup_region @ Region(Interned(RePlaceholder(_), _)),177            )) => self.try_report_trait_placeholder_mismatch(178                (!sub_region.is_named(self.tcx())).then_some(*sub_region),179                cause,180                None,181                Some(*sup_region),182                values,183            ),184185            _ => None,186        }187    }188189    fn try_report_trait_placeholder_mismatch(190        &self,191        vid: Option<Region<'tcx>>,192        cause: &ObligationCause<'tcx>,193        sub_placeholder: Option<Region<'tcx>>,194        sup_placeholder: Option<Region<'tcx>>,195        value_pairs: &ValuePairs<'tcx>,196    ) -> Option<Diag<'tcx>> {197        let (expected_args, found_args, trait_def_id) = match value_pairs {198            ValuePairs::TraitRefs(ExpectedFound { expected, found })199                if expected.def_id == found.def_id =>200            {201                // It's possible that the placeholders come from a binder202                // outside of this value pair. Use `no_bound_vars` as a203                // simple heuristic for that.204                (expected.args, found.args, expected.def_id)205            }206            _ => return None,207        };208209        Some(self.report_trait_placeholder_mismatch(210            vid,211            cause,212            sub_placeholder,213            sup_placeholder,214            trait_def_id,215            expected_args,216            found_args,217        ))218    }219220    // error[E0308]: implementation of `Foo` does not apply to enough lifetimes221    //   --> /home/nmatsakis/tmp/foo.rs:12:5222    //    |223    // 12 |     all::<&'static u32>();224    //    |     ^^^^^^^^^^^^^^^^^^^ lifetime mismatch225    //    |226    //    = note: Due to a where-clause on the function `all`,227    //    = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.228    //    = note: However, the type `T` only implements `...` for some specific lifetime `'2`.229    #[instrument(level = "debug", skip(self))]230    fn report_trait_placeholder_mismatch(231        &self,232        vid: Option<Region<'tcx>>,233        cause: &ObligationCause<'tcx>,234        sub_placeholder: Option<Region<'tcx>>,235        sup_placeholder: Option<Region<'tcx>>,236        trait_def_id: DefId,237        expected_args: GenericArgsRef<'tcx>,238        actual_args: GenericArgsRef<'tcx>,239    ) -> Diag<'tcx> {240        let span = cause.span;241242        let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =243            if let ObligationCauseCode::WhereClause(def_id, span)244            | ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code()245                && def_id != CRATE_DEF_ID.to_def_id()246            {247                (248                    true,249                    Some(span),250                    Some(self.tcx().def_span(def_id)),251                    None,252                    self.tcx().def_path_str(def_id),253                )254            } else {255                (false, None, None, Some(span), String::new())256            };257258        let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(259            self.cx.tcx,260            trait_def_id,261            expected_args,262        ));263        let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(264            self.cx.tcx,265            trait_def_id,266            actual_args,267        ));268269        // Search the expected and actual trait references to see (a)270        // whether the sub/sup placeholders appear in them (sometimes271        // you have a trait ref like `T: Foo<fn(&u8)>`, where the272        // placeholder was created as part of an inner type) and (b)273        // whether the inference variable appears. In each case,274        // assign a counter value in each case if so.275        let mut counter = 0;276        let mut has_sub = None;277        let mut has_sup = None;278279        let mut actual_has_vid = None;280        let mut expected_has_vid = None;281282        self.tcx().for_each_free_region(&expected_trait_ref, |r| {283            if Some(r) == sub_placeholder && has_sub.is_none() {284                has_sub = Some(counter);285                counter += 1;286            } else if Some(r) == sup_placeholder && has_sup.is_none() {287                has_sup = Some(counter);288                counter += 1;289            }290291            if Some(r) == vid && expected_has_vid.is_none() {292                expected_has_vid = Some(counter);293                counter += 1;294            }295        });296297        self.tcx().for_each_free_region(&actual_trait_ref, |r| {298            if Some(r) == vid && actual_has_vid.is_none() {299                actual_has_vid = Some(counter);300                counter += 1;301            }302        });303304        let actual_self_ty_has_vid =305            self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);306307        let expected_self_ty_has_vid =308            self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);309310        let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;311312        debug!(313            ?actual_has_vid,314            ?expected_has_vid,315            ?has_sub,316            ?has_sup,317            ?actual_self_ty_has_vid,318            ?expected_self_ty_has_vid,319        );320321        let actual_impl_expl_notes = self.explain_actual_impl_that_was_found(322            sub_placeholder,323            sup_placeholder,324            has_sub,325            has_sup,326            expected_trait_ref,327            actual_trait_ref,328            vid,329            expected_has_vid,330            actual_has_vid,331            any_self_ty_has_vid,332            leading_ellipsis,333        );334335        self.tcx().dcx().create_err(TraitPlaceholderMismatch {336            span,337            satisfy_span,338            where_span,339            dup_span,340            def_id,341            trait_def_id: self.tcx().def_path_str(trait_def_id),342            actual_impl_expl_notes,343        })344    }345346    /// Add notes with details about the expected and actual trait refs, with attention to cases347    /// when placeholder regions are involved: either the trait or the self type containing348    /// them needs to be mentioned the closest to the placeholders.349    /// This makes the error messages read better, however at the cost of some complexity350    /// due to the number of combinations we have to deal with.351    fn explain_actual_impl_that_was_found(352        &self,353        sub_placeholder: Option<Region<'tcx>>,354        sup_placeholder: Option<Region<'tcx>>,355        has_sub: Option<usize>,356        has_sup: Option<usize>,357        expected_trait_ref: ty::TraitRef<'tcx>,358        actual_trait_ref: ty::TraitRef<'tcx>,359        vid: Option<Region<'tcx>>,360        expected_has_vid: Option<usize>,361        actual_has_vid: Option<usize>,362        any_self_ty_has_vid: bool,363        leading_ellipsis: bool,364    ) -> Vec<ActualImplExplNotes<'tcx>> {365        // The weird thing here with the `maybe_highlighting_region` calls and the366        // the match inside is meant to be like this:367        //368        // - The match checks whether the given things (placeholders, etc) appear369        //   in the types are about to print370        // - Meanwhile, the `maybe_highlighting_region` calls set up371        //   highlights so that, if they do appear, we will replace372        //   them `'0` and whatever. (This replacement takes place373        //   inside the closure given to `maybe_highlighting_region`.)374        //375        // There is some duplication between the calls -- i.e., the376        // `maybe_highlighting_region` checks if (e.g.) `has_sub` is377        // None, an then we check again inside the closure, but this378        // setup sort of minimized the number of calls and so form.379380        let highlight_trait_ref = |trait_ref| Highlighted {381            tcx: self.tcx(),382            highlight: RegionHighlightMode::default(),383            value: trait_ref,384            ns: Namespace::TypeNS,385        };386387        let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();388389        let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);390        expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);391        expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);392393        let passive_voice = match (has_sub, has_sup) {394            (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,395            (None, None) => {396                expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);397                match expected_has_vid {398                    Some(_) => true,399                    None => any_self_ty_has_vid,400                }401            }402        };403404        let (kind, ty_or_sig, trait_path) = if same_self_type {405            let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());406            self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);407408            if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)409            {410                let closure_sig = self_ty.map(|closure| {411                    if let ty::Closure(_, args) = closure.kind() {412                        self.tcx()413                            .signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe)414                    } else {415                        bug!("type is not longer closure");416                    }417                });418                (419                    ActualImplExpectedKind::Signature,420                    TyOrSig::ClosureSig(closure_sig),421                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),422                )423            } else {424                (425                    ActualImplExpectedKind::Other,426                    TyOrSig::Ty(self_ty),427                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),428                )429            }430        } else if passive_voice {431            (432                ActualImplExpectedKind::Passive,433                TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),434                expected_trait_ref.map(|tr| tr.print_only_trait_path()),435            )436        } else {437            (438                ActualImplExpectedKind::Other,439                TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),440                expected_trait_ref.map(|tr| tr.print_only_trait_path()),441            )442        };443444        let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {445            (Some(n1), Some(n2)) => {446                (ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))447            }448            (Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),449            (None, None) => {450                if let Some(n) = expected_has_vid {451                    (ActualImplExpectedLifetimeKind::Some, n, 0)452                } else {453                    (ActualImplExpectedLifetimeKind::Nothing, 0, 0)454                }455            }456        };457458        let note_1 = ActualImplExplNotes::new_expected(459            kind,460            lt_kind,461            leading_ellipsis,462            ty_or_sig,463            trait_path,464            lifetime_1,465            lifetime_2,466        );467468        let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);469        actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);470471        let passive_voice = match actual_has_vid {472            Some(_) => any_self_ty_has_vid,473            None => true,474        };475476        let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());477        let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();478        let has_lifetime = actual_has_vid.is_some();479        let lifetime = actual_has_vid.unwrap_or_default();480481        let note_2 = if same_self_type {482            ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }483        } else if passive_voice {484            ActualImplExplNotes::ButActuallyImplementedForTy {485                trait_path,486                ty,487                has_lifetime,488                lifetime,489            }490        } else {491            ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }492        };493494        vec![note_1, note_2]495    }496}

Code quality findings 2

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// error[E0308]: implementation of `Foo` does not apply to enough lifetimes
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let (expected_args, found_args, trait_def_id) = match value_pairs {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.