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