]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
Rollup merge of #100855 - IsaacCloos:master, r=joshtriplett
[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)
215         | ObligationCauseCode::ExprItemObligation(def_id, ..) =
216             *cause.code()
217         {
218             err.span_label(span, "doesn't satisfy where-clause");
219             err.span_label(
220                 self.tcx().def_span(def_id),
221                 &format!("due to a where-clause on `{}`...", self.tcx().def_path_str(def_id)),
222             );
223             true
224         } else {
225             err.span_label(span, &msg);
226             false
227         };
228
229         let expected_trait_ref = self.infcx.resolve_vars_if_possible(ty::TraitRef {
230             def_id: trait_def_id,
231             substs: expected_substs,
232         });
233         let actual_trait_ref = self
234             .infcx
235             .resolve_vars_if_possible(ty::TraitRef { def_id: trait_def_id, substs: actual_substs });
236
237         // Search the expected and actual trait references to see (a)
238         // whether the sub/sup placeholders appear in them (sometimes
239         // you have a trait ref like `T: Foo<fn(&u8)>`, where the
240         // placeholder was created as part of an inner type) and (b)
241         // whether the inference variable appears. In each case,
242         // assign a counter value in each case if so.
243         let mut counter = 0;
244         let mut has_sub = None;
245         let mut has_sup = None;
246
247         let mut actual_has_vid = None;
248         let mut expected_has_vid = None;
249
250         self.tcx().for_each_free_region(&expected_trait_ref, |r| {
251             if Some(r) == sub_placeholder && has_sub.is_none() {
252                 has_sub = Some(counter);
253                 counter += 1;
254             } else if Some(r) == sup_placeholder && has_sup.is_none() {
255                 has_sup = Some(counter);
256                 counter += 1;
257             }
258
259             if Some(r) == vid && expected_has_vid.is_none() {
260                 expected_has_vid = Some(counter);
261                 counter += 1;
262             }
263         });
264
265         self.tcx().for_each_free_region(&actual_trait_ref, |r| {
266             if Some(r) == vid && actual_has_vid.is_none() {
267                 actual_has_vid = Some(counter);
268                 counter += 1;
269             }
270         });
271
272         let actual_self_ty_has_vid =
273             self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
274
275         let expected_self_ty_has_vid =
276             self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
277
278         let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
279
280         debug!(
281             ?actual_has_vid,
282             ?expected_has_vid,
283             ?has_sub,
284             ?has_sup,
285             ?actual_self_ty_has_vid,
286             ?expected_self_ty_has_vid,
287         );
288
289         self.explain_actual_impl_that_was_found(
290             &mut err,
291             sub_placeholder,
292             sup_placeholder,
293             has_sub,
294             has_sup,
295             expected_trait_ref,
296             actual_trait_ref,
297             vid,
298             expected_has_vid,
299             actual_has_vid,
300             any_self_ty_has_vid,
301             leading_ellipsis,
302         );
303
304         err
305     }
306
307     /// Add notes with details about the expected and actual trait refs, with attention to cases
308     /// when placeholder regions are involved: either the trait or the self type containing
309     /// them needs to be mentioned the closest to the placeholders.
310     /// This makes the error messages read better, however at the cost of some complexity
311     /// due to the number of combinations we have to deal with.
312     fn explain_actual_impl_that_was_found(
313         &self,
314         err: &mut Diagnostic,
315         sub_placeholder: Option<Region<'tcx>>,
316         sup_placeholder: Option<Region<'tcx>>,
317         has_sub: Option<usize>,
318         has_sup: Option<usize>,
319         expected_trait_ref: ty::TraitRef<'tcx>,
320         actual_trait_ref: ty::TraitRef<'tcx>,
321         vid: Option<Region<'tcx>>,
322         expected_has_vid: Option<usize>,
323         actual_has_vid: Option<usize>,
324         any_self_ty_has_vid: bool,
325         leading_ellipsis: bool,
326     ) {
327         // HACK(eddyb) maybe move this in a more central location.
328         #[derive(Copy, Clone)]
329         struct Highlighted<'tcx, T> {
330             tcx: TyCtxt<'tcx>,
331             highlight: RegionHighlightMode<'tcx>,
332             value: T,
333         }
334
335         impl<'tcx, T> Highlighted<'tcx, T> {
336             fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
337                 Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
338             }
339         }
340
341         impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
342         where
343             T: for<'a> Print<
344                 'tcx,
345                 FmtPrinter<'a, 'tcx>,
346                 Error = fmt::Error,
347                 Output = FmtPrinter<'a, 'tcx>,
348             >,
349         {
350             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351                 let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
352                 printer.region_highlight_mode = self.highlight;
353
354                 let s = self.value.print(printer)?.into_buffer();
355                 f.write_str(&s)
356             }
357         }
358
359         // The weird thing here with the `maybe_highlighting_region` calls and the
360         // the match inside is meant to be like this:
361         //
362         // - The match checks whether the given things (placeholders, etc) appear
363         //   in the types are about to print
364         // - Meanwhile, the `maybe_highlighting_region` calls set up
365         //   highlights so that, if they do appear, we will replace
366         //   them `'0` and whatever.  (This replacement takes place
367         //   inside the closure given to `maybe_highlighting_region`.)
368         //
369         // There is some duplication between the calls -- i.e., the
370         // `maybe_highlighting_region` checks if (e.g.) `has_sub` is
371         // None, an then we check again inside the closure, but this
372         // setup sort of minimized the number of calls and so form.
373
374         let highlight_trait_ref = |trait_ref| Highlighted {
375             tcx: self.tcx(),
376             highlight: RegionHighlightMode::new(self.tcx()),
377             value: trait_ref,
378         };
379
380         let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
381
382         let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
383         expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
384         expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
385         err.note(&{
386             let passive_voice = match (has_sub, has_sup) {
387                 (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
388                 (None, None) => {
389                     expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
390                     match expected_has_vid {
391                         Some(_) => true,
392                         None => any_self_ty_has_vid,
393                     }
394                 }
395             };
396
397             let mut note = if same_self_type {
398                 let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
399                 self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
400
401                 if self_ty.value.is_closure()
402                     && self
403                         .tcx()
404                         .fn_trait_kind_from_lang_item(expected_trait_ref.value.def_id)
405                         .is_some()
406                 {
407                     let closure_sig = self_ty.map(|closure| {
408                         if let ty::Closure(_, substs) = closure.kind() {
409                             self.tcx().signature_unclosure(
410                                 substs.as_closure().sig(),
411                                 rustc_hir::Unsafety::Normal,
412                             )
413                         } else {
414                             bug!("type is not longer closure");
415                         }
416                     });
417
418                     format!(
419                         "{}closure with signature `{}` must implement `{}`",
420                         if leading_ellipsis { "..." } else { "" },
421                         closure_sig,
422                         expected_trait_ref.map(|tr| tr.print_only_trait_path()),
423                     )
424                 } else {
425                     format!(
426                         "{}`{}` must implement `{}`",
427                         if leading_ellipsis { "..." } else { "" },
428                         self_ty,
429                         expected_trait_ref.map(|tr| tr.print_only_trait_path()),
430                     )
431                 }
432             } else if passive_voice {
433                 format!(
434                     "{}`{}` would have to be implemented for the type `{}`",
435                     if leading_ellipsis { "..." } else { "" },
436                     expected_trait_ref.map(|tr| tr.print_only_trait_path()),
437                     expected_trait_ref.map(|tr| tr.self_ty()),
438                 )
439             } else {
440                 format!(
441                     "{}`{}` must implement `{}`",
442                     if leading_ellipsis { "..." } else { "" },
443                     expected_trait_ref.map(|tr| tr.self_ty()),
444                     expected_trait_ref.map(|tr| tr.print_only_trait_path()),
445                 )
446             };
447
448             match (has_sub, has_sup) {
449                 (Some(n1), Some(n2)) => {
450                     let _ = write!(
451                         note,
452                         ", for any two lifetimes `'{}` and `'{}`...",
453                         std::cmp::min(n1, n2),
454                         std::cmp::max(n1, n2),
455                     );
456                 }
457                 (Some(n), _) | (_, Some(n)) => {
458                     let _ = write!(note, ", for any lifetime `'{}`...", n,);
459                 }
460                 (None, None) => {
461                     if let Some(n) = expected_has_vid {
462                         let _ = write!(note, ", for some specific lifetime `'{}`...", n,);
463                     }
464                 }
465             }
466
467             note
468         });
469
470         let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
471         actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
472         err.note(&{
473             let passive_voice = match actual_has_vid {
474                 Some(_) => any_self_ty_has_vid,
475                 None => true,
476             };
477
478             let mut note = if same_self_type {
479                 format!(
480                     "...but it actually implements `{}`",
481                     actual_trait_ref.map(|tr| tr.print_only_trait_path()),
482                 )
483             } else if passive_voice {
484                 format!(
485                     "...but `{}` is actually implemented for the type `{}`",
486                     actual_trait_ref.map(|tr| tr.print_only_trait_path()),
487                     actual_trait_ref.map(|tr| tr.self_ty()),
488                 )
489             } else {
490                 format!(
491                     "...but `{}` actually implements `{}`",
492                     actual_trait_ref.map(|tr| tr.self_ty()),
493                     actual_trait_ref.map(|tr| tr.print_only_trait_path()),
494                 )
495             };
496
497             if let Some(n) = actual_has_vid {
498                 let _ = write!(note, ", for some specific lifetime `'{}`", n);
499             }
500
501             note
502         });
503     }
504 }