]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
Made ty_or_sig and trait_path use their actual types instead of String
[rust.git] / compiler / rustc_infer / src / infer / error_reporting / nice_region_error / placeholder_error.rs
1 use crate::errors::{
2     ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes,
3     TraitPlaceholderMismatch, TyOrSig,
4 };
5 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
6 use crate::infer::lexical_region_resolve::RegionResolutionError;
7 use crate::infer::ValuePairs;
8 use crate::infer::{SubregionOrigin, TypeTrace};
9 use crate::traits::{ObligationCause, ObligationCauseCode};
10 use rustc_data_structures::intern::Interned;
11 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
12 use rustc_hir::def::Namespace;
13 use rustc_hir::def_id::DefId;
14 use rustc_middle::ty::error::ExpectedFound;
15 use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode};
16 use rustc_middle::ty::subst::SubstsRef;
17 use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt};
18
19 use std::fmt;
20
21 // HACK(eddyb) maybe move this in a more central location.
22 #[derive(Copy, Clone)]
23 pub struct Highlighted<'tcx, T> {
24     tcx: TyCtxt<'tcx>,
25     highlight: RegionHighlightMode<'tcx>,
26     value: T,
27 }
28
29 impl<'tcx, T> IntoDiagnosticArg for Highlighted<'tcx, T>
30 where
31     T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>, Error = fmt::Error, Output = FmtPrinter<'a, 'tcx>>,
32 {
33     fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
34         rustc_errors::DiagnosticArgValue::Str(self.to_string().into())
35     }
36 }
37
38 impl<'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) }
41     }
42 }
43
44 impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
45 where
46     T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>, Error = fmt::Error, Output = FmtPrinter<'a, 'tcx>>,
47 {
48     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49         let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
50         printer.region_highlight_mode = self.highlight;
51
52         let s = self.value.print(printer)?.into_buffer();
53         f.write_str(&s)
54     }
55 }
56
57 impl<'tcx> NiceRegionError<'_, 'tcx> {
58     /// When given a `ConcreteFailure` for a function with arguments containing a named region and
59     /// an anonymous region, emit a descriptive diagnostic error.
60     pub(super) fn try_report_placeholder_conflict(
61         &self,
62     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
63         match &self.error {
64             ///////////////////////////////////////////////////////////////////////////
65             // NB. The ordering of cases in this match is very
66             // sensitive, because we are often matching against
67             // specific cases and then using an `_` to match all
68             // others.
69
70             ///////////////////////////////////////////////////////////////////////////
71             // Check for errors from comparing trait failures -- first
72             // with two placeholders, then with one.
73             Some(RegionResolutionError::SubSupConflict(
74                 vid,
75                 _,
76                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
77                 sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
78                 _,
79                 sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
80                 _,
81             )) => self.try_report_trait_placeholder_mismatch(
82                 Some(self.tcx().mk_region(ReVar(*vid))),
83                 cause,
84                 Some(*sub_placeholder),
85                 Some(*sup_placeholder),
86                 values,
87             ),
88
89             Some(RegionResolutionError::SubSupConflict(
90                 vid,
91                 _,
92                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
93                 sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
94                 _,
95                 _,
96                 _,
97             )) => self.try_report_trait_placeholder_mismatch(
98                 Some(self.tcx().mk_region(ReVar(*vid))),
99                 cause,
100                 Some(*sub_placeholder),
101                 None,
102                 values,
103             ),
104
105             Some(RegionResolutionError::SubSupConflict(
106                 vid,
107                 _,
108                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
109                 _,
110                 _,
111                 sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
112                 _,
113             )) => self.try_report_trait_placeholder_mismatch(
114                 Some(self.tcx().mk_region(ReVar(*vid))),
115                 cause,
116                 None,
117                 Some(*sup_placeholder),
118                 values,
119             ),
120
121             Some(RegionResolutionError::SubSupConflict(
122                 vid,
123                 _,
124                 _,
125                 _,
126                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
127                 sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
128                 _,
129             )) => self.try_report_trait_placeholder_mismatch(
130                 Some(self.tcx().mk_region(ReVar(*vid))),
131                 cause,
132                 None,
133                 Some(*sup_placeholder),
134                 values,
135             ),
136
137             Some(RegionResolutionError::UpperBoundUniverseConflict(
138                 vid,
139                 _,
140                 _,
141                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
142                 sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
143             )) => self.try_report_trait_placeholder_mismatch(
144                 Some(self.tcx().mk_region(ReVar(*vid))),
145                 cause,
146                 None,
147                 Some(*sup_placeholder),
148                 values,
149             ),
150
151             Some(RegionResolutionError::ConcreteFailure(
152                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
153                 sub_region @ Region(Interned(RePlaceholder(_), _)),
154                 sup_region @ Region(Interned(RePlaceholder(_), _)),
155             )) => self.try_report_trait_placeholder_mismatch(
156                 None,
157                 cause,
158                 Some(*sub_region),
159                 Some(*sup_region),
160                 values,
161             ),
162
163             Some(RegionResolutionError::ConcreteFailure(
164                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
165                 sub_region @ Region(Interned(RePlaceholder(_), _)),
166                 sup_region,
167             )) => self.try_report_trait_placeholder_mismatch(
168                 (!sup_region.has_name()).then_some(*sup_region),
169                 cause,
170                 Some(*sub_region),
171                 None,
172                 values,
173             ),
174
175             Some(RegionResolutionError::ConcreteFailure(
176                 SubregionOrigin::Subtype(box TypeTrace { cause, values }),
177                 sub_region,
178                 sup_region @ Region(Interned(RePlaceholder(_), _)),
179             )) => self.try_report_trait_placeholder_mismatch(
180                 (!sub_region.has_name()).then_some(*sub_region),
181                 cause,
182                 None,
183                 Some(*sup_region),
184                 values,
185             ),
186
187             _ => None,
188         }
189     }
190
191     fn try_report_trait_placeholder_mismatch(
192         &self,
193         vid: Option<Region<'tcx>>,
194         cause: &ObligationCause<'tcx>,
195         sub_placeholder: Option<Region<'tcx>>,
196         sup_placeholder: Option<Region<'tcx>>,
197         value_pairs: &ValuePairs<'tcx>,
198     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
199         let (expected_substs, found_substs, trait_def_id) = match value_pairs {
200             ValuePairs::TraitRefs(ExpectedFound { expected, found })
201                 if expected.def_id == found.def_id =>
202             {
203                 (expected.substs, found.substs, expected.def_id)
204             }
205             ValuePairs::PolyTraitRefs(ExpectedFound { expected, found })
206                 if expected.def_id() == found.def_id() =>
207             {
208                 // It's possible that the placeholders come from a binder
209                 // outside of this value pair. Use `no_bound_vars` as a
210                 // simple heuristic for that.
211                 (expected.no_bound_vars()?.substs, found.no_bound_vars()?.substs, expected.def_id())
212             }
213             _ => return None,
214         };
215
216         Some(self.report_trait_placeholder_mismatch(
217             vid,
218             cause,
219             sub_placeholder,
220             sup_placeholder,
221             trait_def_id,
222             expected_substs,
223             found_substs,
224         ))
225     }
226
227     // error[E0308]: implementation of `Foo` does not apply to enough lifetimes
228     //   --> /home/nmatsakis/tmp/foo.rs:12:5
229     //    |
230     // 12 |     all::<&'static u32>();
231     //    |     ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
232     //    |
233     //    = note: Due to a where-clause on the function `all`,
234     //    = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
235     //    = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
236     #[instrument(level = "debug", skip(self))]
237     fn report_trait_placeholder_mismatch(
238         &self,
239         vid: Option<Region<'tcx>>,
240         cause: &ObligationCause<'tcx>,
241         sub_placeholder: Option<Region<'tcx>>,
242         sup_placeholder: Option<Region<'tcx>>,
243         trait_def_id: DefId,
244         expected_substs: SubstsRef<'tcx>,
245         actual_substs: SubstsRef<'tcx>,
246     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
247         let span = cause.span();
248
249         let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
250             if let ObligationCauseCode::ItemObligation(def_id)
251             | ObligationCauseCode::ExprItemObligation(def_id, ..) = *cause.code()
252             {
253                 (
254                     true,
255                     Some(span),
256                     Some(self.tcx().def_span(def_id)),
257                     None,
258                     self.tcx().def_path_str(def_id),
259                 )
260             } else {
261                 (false, None, None, Some(span), String::new())
262             };
263
264         let expected_trait_ref = self
265             .cx
266             .resolve_vars_if_possible(self.cx.tcx.mk_trait_ref(trait_def_id, expected_substs));
267         let actual_trait_ref =
268             self.cx.resolve_vars_if_possible(self.cx.tcx.mk_trait_ref(trait_def_id, actual_substs));
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         let diag = 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         self.tcx().sess.create_err(diag)
347     }
348
349     /// Add notes with details about the expected and actual trait refs, with attention to cases
350     /// when placeholder regions are involved: either the trait or the self type containing
351     /// them needs to be mentioned the closest to the placeholders.
352     /// This makes the error messages read better, however at the cost of some complexity
353     /// due to the number of combinations we have to deal with.
354     fn explain_actual_impl_that_was_found(
355         &self,
356         sub_placeholder: Option<Region<'tcx>>,
357         sup_placeholder: Option<Region<'tcx>>,
358         has_sub: Option<usize>,
359         has_sup: Option<usize>,
360         expected_trait_ref: ty::TraitRef<'tcx>,
361         actual_trait_ref: ty::TraitRef<'tcx>,
362         vid: Option<Region<'tcx>>,
363         expected_has_vid: Option<usize>,
364         actual_has_vid: Option<usize>,
365         any_self_ty_has_vid: bool,
366         leading_ellipsis: bool,
367     ) -> Vec<ActualImplExplNotes<'tcx>> {
368         // The weird thing here with the `maybe_highlighting_region` calls and the
369         // the match inside is meant to be like this:
370         //
371         // - The match checks whether the given things (placeholders, etc) appear
372         //   in the types are about to print
373         // - Meanwhile, the `maybe_highlighting_region` calls set up
374         //   highlights so that, if they do appear, we will replace
375         //   them `'0` and whatever.  (This replacement takes place
376         //   inside the closure given to `maybe_highlighting_region`.)
377         //
378         // There is some duplication between the calls -- i.e., the
379         // `maybe_highlighting_region` checks if (e.g.) `has_sub` is
380         // None, an then we check again inside the closure, but this
381         // setup sort of minimized the number of calls and so form.
382
383         let highlight_trait_ref = |trait_ref| Highlighted {
384             tcx: self.tcx(),
385             highlight: RegionHighlightMode::new(self.tcx()),
386             value: trait_ref,
387         };
388
389         let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
390
391         let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
392         expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
393         expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
394
395         let passive_voice = match (has_sub, has_sup) {
396             (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
397             (None, None) => {
398                 expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
399                 match expected_has_vid {
400                     Some(_) => true,
401                     None => any_self_ty_has_vid,
402                 }
403             }
404         };
405
406         let (kind, ty_or_sig, trait_path) = if same_self_type {
407             let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
408             self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
409
410             if self_ty.value.is_closure()
411                 && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
412             {
413                 let closure_sig = self_ty.map(|closure| {
414                     if let ty::Closure(_, substs) = closure.kind() {
415                         self.tcx().signature_unclosure(
416                             substs.as_closure().sig(),
417                             rustc_hir::Unsafety::Normal,
418                         )
419                     } else {
420                         bug!("type is not longer closure");
421                     }
422                 });
423                 (
424                     ActualImplExpectedKind::Signature,
425                     TyOrSig::ClosureSig(closure_sig),
426                     expected_trait_ref.map(|tr| tr.print_only_trait_path()),
427                 )
428             } else {
429                 (
430                     ActualImplExpectedKind::Other,
431                     TyOrSig::Ty(self_ty),
432                     expected_trait_ref.map(|tr| tr.print_only_trait_path()),
433                 )
434             }
435         } else if passive_voice {
436             (
437                 ActualImplExpectedKind::Passive,
438                 TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
439                 expected_trait_ref.map(|tr| tr.print_only_trait_path()),
440             )
441         } else {
442             (
443                 ActualImplExpectedKind::Other,
444                 TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
445                 expected_trait_ref.map(|tr| tr.print_only_trait_path()),
446             )
447         };
448
449         let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {
450             (Some(n1), Some(n2)) => {
451                 (ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))
452             }
453             (Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),
454             (None, None) => {
455                 if let Some(n) = expected_has_vid {
456                     (ActualImplExpectedLifetimeKind::Some, n, 0)
457                 } else {
458                     (ActualImplExpectedLifetimeKind::Nothing, 0, 0)
459                 }
460             }
461         };
462
463         let note_1 = ActualImplExplNotes::new_expected(
464             kind,
465             lt_kind,
466             leading_ellipsis,
467             ty_or_sig,
468             trait_path,
469             lifetime_1,
470             lifetime_2,
471         );
472
473         let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
474         actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
475
476         let passive_voice = match actual_has_vid {
477             Some(_) => any_self_ty_has_vid,
478             None => true,
479         };
480
481         let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());
482         let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();
483         let has_lifetime = actual_has_vid.is_some();
484         let lifetime = actual_has_vid.unwrap_or_default();
485
486         let note_2 = if same_self_type {
487             ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }
488         } else if passive_voice {
489             ActualImplExplNotes::ButActuallyImplementedForTy {
490                 trait_path,
491                 ty,
492                 has_lifetime,
493                 lifetime,
494             }
495         } else {
496             ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }
497         };
498
499         vec![note_1, note_2]
500     }
501 }