rustc_trait_selection/error_reporting/infer/nice_region_error/
placeholder_error.rs

1use std::fmt;
2
3use 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};
12
13use 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};
20
21// HACK(eddyb) maybe move this in a more central location.
22#[derive(Copy, Clone)]
23pub struct Highlighted<'tcx, T> {
24    pub tcx: TyCtxt<'tcx>,
25    pub highlight: RegionHighlightMode<'tcx>,
26    pub value: T,
27    pub ns: Namespace,
28}
29
30impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>
31where
32    T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
33{
34    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
35        rustc_errors::DiagArgValue::Str(self.to_string().into())
36    }
37}
38
39impl<'tcx, T> Highlighted<'tcx, T> {
40    fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
41        Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value), ns: self.ns }
42    }
43}
44
45impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
46where
47    T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
48{
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        let mut printer = ty::print::FmtPrinter::new(self.tcx, self.ns);
51        printer.region_highlight_mode = self.highlight;
52
53        self.value.print(&mut printer)?;
54        f.write_str(&printer.into_buffer())
55    }
56}
57
58impl<'tcx> NiceRegionError<'_, 'tcx> {
59    /// When given a `ConcreteFailure` for a function with arguments containing a named region and
60    /// an anonymous region, emit a descriptive diagnostic error.
61    pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> {
62        match &self.error {
63            ///////////////////////////////////////////////////////////////////////////
64            // NB. The ordering of cases in this match is very
65            // sensitive, because we are often matching against
66            // specific cases and then using an `_` to match all
67            // others.
68
69            ///////////////////////////////////////////////////////////////////////////
70            // Check for errors from comparing trait failures -- first
71            // with two placeholders, then with one.
72            Some(RegionResolutionError::SubSupConflict(
73                vid,
74                _,
75                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
76                sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
77                _,
78                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
79                _,
80            )) => self.try_report_trait_placeholder_mismatch(
81                Some(ty::Region::new_var(self.tcx(), *vid)),
82                cause,
83                Some(*sub_placeholder),
84                Some(*sup_placeholder),
85                values,
86            ),
87
88            Some(RegionResolutionError::SubSupConflict(
89                vid,
90                _,
91                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
92                sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
93                _,
94                _,
95                _,
96            )) => self.try_report_trait_placeholder_mismatch(
97                Some(ty::Region::new_var(self.tcx(), *vid)),
98                cause,
99                Some(*sub_placeholder),
100                None,
101                values,
102            ),
103
104            Some(RegionResolutionError::SubSupConflict(
105                vid,
106                _,
107                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
108                _,
109                _,
110                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
111                _,
112            )) => self.try_report_trait_placeholder_mismatch(
113                Some(ty::Region::new_var(self.tcx(), *vid)),
114                cause,
115                None,
116                Some(*sup_placeholder),
117                values,
118            ),
119
120            Some(RegionResolutionError::SubSupConflict(
121                vid,
122                _,
123                _,
124                _,
125                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
126                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
127                _,
128            )) => self.try_report_trait_placeholder_mismatch(
129                Some(ty::Region::new_var(self.tcx(), *vid)),
130                cause,
131                None,
132                Some(*sup_placeholder),
133                values,
134            ),
135
136            Some(RegionResolutionError::UpperBoundUniverseConflict(
137                vid,
138                _,
139                _,
140                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
141                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
142            )) => self.try_report_trait_placeholder_mismatch(
143                Some(ty::Region::new_var(self.tcx(), *vid)),
144                cause,
145                None,
146                Some(*sup_placeholder),
147                values,
148            ),
149
150            Some(RegionResolutionError::ConcreteFailure(
151                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
152                sub_region @ Region(Interned(RePlaceholder(_), _)),
153                sup_region @ Region(Interned(RePlaceholder(_), _)),
154            )) => self.try_report_trait_placeholder_mismatch(
155                None,
156                cause,
157                Some(*sub_region),
158                Some(*sup_region),
159                values,
160            ),
161
162            Some(RegionResolutionError::ConcreteFailure(
163                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
164                sub_region @ Region(Interned(RePlaceholder(_), _)),
165                sup_region,
166            )) => self.try_report_trait_placeholder_mismatch(
167                (!sup_region.has_name()).then_some(*sup_region),
168                cause,
169                Some(*sub_region),
170                None,
171                values,
172            ),
173
174            Some(RegionResolutionError::ConcreteFailure(
175                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
176                sub_region,
177                sup_region @ Region(Interned(RePlaceholder(_), _)),
178            )) => self.try_report_trait_placeholder_mismatch(
179                (!sub_region.has_name()).then_some(*sub_region),
180                cause,
181                None,
182                Some(*sup_region),
183                values,
184            ),
185
186            _ => None,
187        }
188    }
189
190    fn try_report_trait_placeholder_mismatch(
191        &self,
192        vid: Option<Region<'tcx>>,
193        cause: &ObligationCause<'tcx>,
194        sub_placeholder: Option<Region<'tcx>>,
195        sup_placeholder: Option<Region<'tcx>>,
196        value_pairs: &ValuePairs<'tcx>,
197    ) -> Option<Diag<'tcx>> {
198        let (expected_args, found_args, trait_def_id) = match value_pairs {
199            ValuePairs::TraitRefs(ExpectedFound { expected, found })
200                if expected.def_id == found.def_id =>
201            {
202                // It's possible that the placeholders come from a binder
203                // outside of this value pair. Use `no_bound_vars` as a
204                // simple heuristic for that.
205                (expected.args, found.args, expected.def_id)
206            }
207            _ => return None,
208        };
209
210        Some(self.report_trait_placeholder_mismatch(
211            vid,
212            cause,
213            sub_placeholder,
214            sup_placeholder,
215            trait_def_id,
216            expected_args,
217            found_args,
218        ))
219    }
220
221    // error[E0308]: implementation of `Foo` does not apply to enough lifetimes
222    //   --> /home/nmatsakis/tmp/foo.rs:12:5
223    //    |
224    // 12 |     all::<&'static u32>();
225    //    |     ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
226    //    |
227    //    = note: Due to a where-clause on the function `all`,
228    //    = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
229    //    = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
230    #[instrument(level = "debug", skip(self))]
231    fn report_trait_placeholder_mismatch(
232        &self,
233        vid: Option<Region<'tcx>>,
234        cause: &ObligationCause<'tcx>,
235        sub_placeholder: Option<Region<'tcx>>,
236        sup_placeholder: Option<Region<'tcx>>,
237        trait_def_id: DefId,
238        expected_args: GenericArgsRef<'tcx>,
239        actual_args: GenericArgsRef<'tcx>,
240    ) -> Diag<'tcx> {
241        let span = cause.span;
242
243        let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
244            if let ObligationCauseCode::WhereClause(def_id, span)
245            | ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code()
246                && def_id != CRATE_DEF_ID.to_def_id()
247            {
248                (
249                    true,
250                    Some(span),
251                    Some(self.tcx().def_span(def_id)),
252                    None,
253                    self.tcx().def_path_str(def_id),
254                )
255            } else {
256                (false, None, None, Some(span), String::new())
257            };
258
259        let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
260            self.cx.tcx,
261            trait_def_id,
262            expected_args,
263        ));
264        let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
265            self.cx.tcx,
266            trait_def_id,
267            actual_args,
268        ));
269
270        // Search the expected and actual trait references to see (a)
271        // whether the sub/sup placeholders appear in them (sometimes
272        // you have a trait ref like `T: Foo<fn(&u8)>`, where the
273        // placeholder was created as part of an inner type) and (b)
274        // whether the inference variable appears. In each case,
275        // assign a counter value in each case if so.
276        let mut counter = 0;
277        let mut has_sub = None;
278        let mut has_sup = None;
279
280        let mut actual_has_vid = None;
281        let mut expected_has_vid = None;
282
283        self.tcx().for_each_free_region(&expected_trait_ref, |r| {
284            if Some(r) == sub_placeholder && has_sub.is_none() {
285                has_sub = Some(counter);
286                counter += 1;
287            } else if Some(r) == sup_placeholder && has_sup.is_none() {
288                has_sup = Some(counter);
289                counter += 1;
290            }
291
292            if Some(r) == vid && expected_has_vid.is_none() {
293                expected_has_vid = Some(counter);
294                counter += 1;
295            }
296        });
297
298        self.tcx().for_each_free_region(&actual_trait_ref, |r| {
299            if Some(r) == vid && actual_has_vid.is_none() {
300                actual_has_vid = Some(counter);
301                counter += 1;
302            }
303        });
304
305        let actual_self_ty_has_vid =
306            self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
307
308        let expected_self_ty_has_vid =
309            self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
310
311        let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
312
313        debug!(
314            ?actual_has_vid,
315            ?expected_has_vid,
316            ?has_sub,
317            ?has_sup,
318            ?actual_self_ty_has_vid,
319            ?expected_self_ty_has_vid,
320        );
321
322        let actual_impl_expl_notes = self.explain_actual_impl_that_was_found(
323            sub_placeholder,
324            sup_placeholder,
325            has_sub,
326            has_sup,
327            expected_trait_ref,
328            actual_trait_ref,
329            vid,
330            expected_has_vid,
331            actual_has_vid,
332            any_self_ty_has_vid,
333            leading_ellipsis,
334        );
335
336        self.tcx().dcx().create_err(TraitPlaceholderMismatch {
337            span,
338            satisfy_span,
339            where_span,
340            dup_span,
341            def_id,
342            trait_def_id: self.tcx().def_path_str(trait_def_id),
343            actual_impl_expl_notes,
344        })
345    }
346
347    /// Add notes with details about the expected and actual trait refs, with attention to cases
348    /// when placeholder regions are involved: either the trait or the self type containing
349    /// them needs to be mentioned the closest to the placeholders.
350    /// This makes the error messages read better, however at the cost of some complexity
351    /// due to the number of combinations we have to deal with.
352    fn explain_actual_impl_that_was_found(
353        &self,
354        sub_placeholder: Option<Region<'tcx>>,
355        sup_placeholder: Option<Region<'tcx>>,
356        has_sub: Option<usize>,
357        has_sup: Option<usize>,
358        expected_trait_ref: ty::TraitRef<'tcx>,
359        actual_trait_ref: ty::TraitRef<'tcx>,
360        vid: Option<Region<'tcx>>,
361        expected_has_vid: Option<usize>,
362        actual_has_vid: Option<usize>,
363        any_self_ty_has_vid: bool,
364        leading_ellipsis: bool,
365    ) -> Vec<ActualImplExplNotes<'tcx>> {
366        // The weird thing here with the `maybe_highlighting_region` calls and the
367        // the match inside is meant to be like this:
368        //
369        // - The match checks whether the given things (placeholders, etc) appear
370        //   in the types are about to print
371        // - Meanwhile, the `maybe_highlighting_region` calls set up
372        //   highlights so that, if they do appear, we will replace
373        //   them `'0` and whatever. (This replacement takes place
374        //   inside the closure given to `maybe_highlighting_region`.)
375        //
376        // There is some duplication between the calls -- i.e., the
377        // `maybe_highlighting_region` checks if (e.g.) `has_sub` is
378        // None, an then we check again inside the closure, but this
379        // setup sort of minimized the number of calls and so form.
380
381        let highlight_trait_ref = |trait_ref| Highlighted {
382            tcx: self.tcx(),
383            highlight: RegionHighlightMode::default(),
384            value: trait_ref,
385            ns: Namespace::TypeNS,
386        };
387
388        let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
389
390        let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
391        expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
392        expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
393
394        let passive_voice = match (has_sub, has_sup) {
395            (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
396            (None, None) => {
397                expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
398                match expected_has_vid {
399                    Some(_) => true,
400                    None => any_self_ty_has_vid,
401                }
402            }
403        };
404
405        let (kind, ty_or_sig, trait_path) = if same_self_type {
406            let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
407            self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
408
409            if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
410            {
411                let closure_sig = self_ty.map(|closure| {
412                    if let ty::Closure(_, args) = closure.kind() {
413                        self.tcx()
414                            .signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe)
415                    } else {
416                        bug!("type is not longer closure");
417                    }
418                });
419                (
420                    ActualImplExpectedKind::Signature,
421                    TyOrSig::ClosureSig(closure_sig),
422                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),
423                )
424            } else {
425                (
426                    ActualImplExpectedKind::Other,
427                    TyOrSig::Ty(self_ty),
428                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),
429                )
430            }
431        } else if passive_voice {
432            (
433                ActualImplExpectedKind::Passive,
434                TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
435                expected_trait_ref.map(|tr| tr.print_only_trait_path()),
436            )
437        } else {
438            (
439                ActualImplExpectedKind::Other,
440                TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
441                expected_trait_ref.map(|tr| tr.print_only_trait_path()),
442            )
443        };
444
445        let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {
446            (Some(n1), Some(n2)) => {
447                (ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))
448            }
449            (Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),
450            (None, None) => {
451                if let Some(n) = expected_has_vid {
452                    (ActualImplExpectedLifetimeKind::Some, n, 0)
453                } else {
454                    (ActualImplExpectedLifetimeKind::Nothing, 0, 0)
455                }
456            }
457        };
458
459        let note_1 = ActualImplExplNotes::new_expected(
460            kind,
461            lt_kind,
462            leading_ellipsis,
463            ty_or_sig,
464            trait_path,
465            lifetime_1,
466            lifetime_2,
467        );
468
469        let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
470        actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
471
472        let passive_voice = match actual_has_vid {
473            Some(_) => any_self_ty_has_vid,
474            None => true,
475        };
476
477        let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());
478        let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();
479        let has_lifetime = actual_has_vid.is_some();
480        let lifetime = actual_has_vid.unwrap_or_default();
481
482        let note_2 = if same_self_type {
483            ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }
484        } else if passive_voice {
485            ActualImplExplNotes::ButActuallyImplementedForTy {
486                trait_path,
487                ty,
488                has_lifetime,
489                lifetime,
490            }
491        } else {
492            ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }
493        };
494
495        vec![note_1, note_2]
496    }
497}