]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
Make select_* methods return Vec for TraitEngine
[rust.git] / compiler / rustc_borrowck / src / diagnostics / bound_region_errors.rs
1 use rustc_errors::DiagnosticBuilder;
2 use rustc_infer::infer::canonical::Canonical;
3 use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
4 use rustc_infer::infer::region_constraints::Constraint;
5 use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _};
6 use rustc_infer::traits::{Normalized, ObligationCause, TraitEngine, TraitEngineExt};
7 use rustc_middle::ty::error::TypeError;
8 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
9 use rustc_span::Span;
10 use rustc_trait_selection::traits::query::type_op;
11 use rustc_trait_selection::traits::{SelectionContext, TraitEngineExt as _};
12 use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause};
13
14 use std::fmt;
15 use std::rc::Rc;
16
17 use crate::region_infer::values::RegionElement;
18 use crate::MirBorrowckCtxt;
19
20 #[derive(Clone)]
21 crate struct UniverseInfo<'tcx>(UniverseInfoInner<'tcx>);
22
23 /// What operation a universe was created for.
24 #[derive(Clone)]
25 enum UniverseInfoInner<'tcx> {
26     /// Relating two types which have binders.
27     RelateTys { expected: Ty<'tcx>, found: Ty<'tcx> },
28     /// Created from performing a `TypeOp`.
29     TypeOp(Rc<dyn TypeOpInfo<'tcx> + 'tcx>),
30     /// Any other reason.
31     Other,
32 }
33
34 impl UniverseInfo<'tcx> {
35     crate fn other() -> UniverseInfo<'tcx> {
36         UniverseInfo(UniverseInfoInner::Other)
37     }
38
39     crate fn relate(expected: Ty<'tcx>, found: Ty<'tcx>) -> UniverseInfo<'tcx> {
40         UniverseInfo(UniverseInfoInner::RelateTys { expected, found })
41     }
42
43     crate fn report_error(
44         &self,
45         mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
46         placeholder: ty::PlaceholderRegion,
47         error_element: RegionElement,
48         cause: ObligationCause<'tcx>,
49     ) {
50         match self.0 {
51             UniverseInfoInner::RelateTys { expected, found } => {
52                 let err = mbcx.infcx.report_mismatched_types(
53                     &cause,
54                     expected,
55                     found,
56                     TypeError::RegionsPlaceholderMismatch,
57                 );
58                 err.buffer(&mut mbcx.errors_buffer);
59             }
60             UniverseInfoInner::TypeOp(ref type_op_info) => {
61                 type_op_info.report_error(mbcx, placeholder, error_element, cause);
62             }
63             UniverseInfoInner::Other => {
64                 // FIXME: This error message isn't great, but it doesn't show
65                 // up in the existing UI tests. Consider investigating this
66                 // some more.
67                 mbcx.infcx
68                     .tcx
69                     .sess
70                     .struct_span_err(cause.span, "higher-ranked subtype error")
71                     .buffer(&mut mbcx.errors_buffer);
72             }
73         }
74     }
75 }
76
77 crate trait ToUniverseInfo<'tcx> {
78     fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>;
79 }
80
81 impl<'tcx> ToUniverseInfo<'tcx>
82     for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>
83 {
84     fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
85         UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(PredicateQuery {
86             canonical_query: self,
87             base_universe,
88         })))
89     }
90 }
91
92 impl<'tcx, T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx> ToUniverseInfo<'tcx>
93     for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>
94 {
95     fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
96         UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(NormalizeQuery {
97             canonical_query: self,
98             base_universe,
99         })))
100     }
101 }
102
103 impl<'tcx> ToUniverseInfo<'tcx>
104     for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>
105 {
106     fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
107         UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(AscribeUserTypeQuery {
108             canonical_query: self,
109             base_universe,
110         })))
111     }
112 }
113
114 impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F, G>> {
115     fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
116         // We can't rerun custom type ops.
117         UniverseInfo::other()
118     }
119 }
120
121 #[allow(unused_lifetimes)]
122 trait TypeOpInfo<'tcx> {
123     /// Returns an error to be reported if rerunning the type op fails to
124     /// recover the error's cause.
125     fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
126
127     fn base_universe(&self) -> ty::UniverseIndex;
128
129     fn nice_error(
130         &self,
131         tcx: TyCtxt<'tcx>,
132         cause: ObligationCause<'tcx>,
133         placeholder_region: ty::Region<'tcx>,
134         error_region: Option<ty::Region<'tcx>>,
135     ) -> Option<DiagnosticBuilder<'tcx>>;
136
137     fn report_error(
138         &self,
139         mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
140         placeholder: ty::PlaceholderRegion,
141         error_element: RegionElement,
142         cause: ObligationCause<'tcx>,
143     ) {
144         let tcx = mbcx.infcx.tcx;
145         let base_universe = self.base_universe();
146
147         let adjusted_universe = if let Some(adjusted) =
148             placeholder.universe.as_u32().checked_sub(base_universe.as_u32())
149         {
150             adjusted
151         } else {
152             self.fallback_error(tcx, cause.span).buffer(&mut mbcx.errors_buffer);
153             return;
154         };
155
156         let placeholder_region = tcx.mk_region(ty::RePlaceholder(ty::Placeholder {
157             name: placeholder.name,
158             universe: adjusted_universe.into(),
159         }));
160
161         let error_region =
162             if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
163                 let adjusted_universe =
164                     error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
165                 adjusted_universe.map(|adjusted| {
166                     tcx.mk_region(ty::RePlaceholder(ty::Placeholder {
167                         name: error_placeholder.name,
168                         universe: adjusted.into(),
169                     }))
170                 })
171             } else {
172                 None
173             };
174
175         debug!(?placeholder_region);
176
177         let span = cause.span;
178         let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region);
179
180         if let Some(nice_error) = nice_error {
181             nice_error.buffer(&mut mbcx.errors_buffer);
182         } else {
183             self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer);
184         }
185     }
186 }
187
188 struct PredicateQuery<'tcx> {
189     canonical_query:
190         Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>,
191     base_universe: ty::UniverseIndex,
192 }
193
194 impl TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
195     fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
196         let mut err = tcx.sess.struct_span_err(span, "higher-ranked lifetime error");
197         err.note(&format!("could not prove {}", self.canonical_query.value.value.predicate));
198         err
199     }
200
201     fn base_universe(&self) -> ty::UniverseIndex {
202         self.base_universe
203     }
204
205     fn nice_error(
206         &self,
207         tcx: TyCtxt<'tcx>,
208         cause: ObligationCause<'tcx>,
209         placeholder_region: ty::Region<'tcx>,
210         error_region: Option<ty::Region<'tcx>>,
211     ) -> Option<DiagnosticBuilder<'tcx>> {
212         tcx.infer_ctxt().enter_with_canonical(
213             cause.span,
214             &self.canonical_query,
215             |ref infcx, key, _| {
216                 let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
217                 type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
218                 try_extract_error_from_fulfill_cx(
219                     fulfill_cx,
220                     infcx,
221                     placeholder_region,
222                     error_region,
223                 )
224             },
225         )
226     }
227 }
228
229 struct NormalizeQuery<'tcx, T> {
230     canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>,
231     base_universe: ty::UniverseIndex,
232 }
233
234 impl<T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T>
235 where
236     T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx,
237 {
238     fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
239         let mut err = tcx.sess.struct_span_err(span, "higher-ranked lifetime error");
240         err.note(&format!("could not normalize `{}`", self.canonical_query.value.value.value));
241         err
242     }
243
244     fn base_universe(&self) -> ty::UniverseIndex {
245         self.base_universe
246     }
247
248     fn nice_error(
249         &self,
250         tcx: TyCtxt<'tcx>,
251         cause: ObligationCause<'tcx>,
252         placeholder_region: ty::Region<'tcx>,
253         error_region: Option<ty::Region<'tcx>>,
254     ) -> Option<DiagnosticBuilder<'tcx>> {
255         tcx.infer_ctxt().enter_with_canonical(
256             cause.span,
257             &self.canonical_query,
258             |ref infcx, key, _| {
259                 let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
260
261                 let mut selcx = SelectionContext::new(infcx);
262
263                 // FIXME(lqd): Unify and de-duplicate the following with the actual
264                 // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
265                 // `ObligationCause`. The normalization results are currently different between
266                 // `AtExt::normalize` used in the query and `normalize` called below: the former fails
267                 // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
268                 // after #85499 lands to see if its fixes have erased this difference.
269                 let (param_env, value) = key.into_parts();
270                 let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
271                     &mut selcx,
272                     param_env,
273                     cause,
274                     value.value,
275                 );
276                 fulfill_cx.register_predicate_obligations(infcx, obligations);
277
278                 try_extract_error_from_fulfill_cx(
279                     fulfill_cx,
280                     infcx,
281                     placeholder_region,
282                     error_region,
283                 )
284             },
285         )
286     }
287 }
288
289 struct AscribeUserTypeQuery<'tcx> {
290     canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>,
291     base_universe: ty::UniverseIndex,
292 }
293
294 impl TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
295     fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
296         // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
297         // and is only the fallback when the nice error fails. Consider improving this some more.
298         tcx.sess.struct_span_err(span, "higher-ranked lifetime error")
299     }
300
301     fn base_universe(&self) -> ty::UniverseIndex {
302         self.base_universe
303     }
304
305     fn nice_error(
306         &self,
307         tcx: TyCtxt<'tcx>,
308         cause: ObligationCause<'tcx>,
309         placeholder_region: ty::Region<'tcx>,
310         error_region: Option<ty::Region<'tcx>>,
311     ) -> Option<DiagnosticBuilder<'tcx>> {
312         tcx.infer_ctxt().enter_with_canonical(
313             cause.span,
314             &self.canonical_query,
315             |ref infcx, key, _| {
316                 let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
317                 type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span))
318                     .ok()?;
319                 try_extract_error_from_fulfill_cx(
320                     fulfill_cx,
321                     infcx,
322                     placeholder_region,
323                     error_region,
324                 )
325             },
326         )
327     }
328 }
329
330 #[instrument(skip(fulfill_cx, infcx), level = "debug")]
331 fn try_extract_error_from_fulfill_cx<'tcx>(
332     mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
333     infcx: &InferCtxt<'_, 'tcx>,
334     placeholder_region: ty::Region<'tcx>,
335     error_region: Option<ty::Region<'tcx>>,
336 ) -> Option<DiagnosticBuilder<'tcx>> {
337     let tcx = infcx.tcx;
338
339     // We generally shouldn't have errors here because the query was
340     // already run, but there's no point using `delay_span_bug`
341     // when we're going to emit an error here anyway.
342     let _errors = fulfill_cx.select_all_or_error(infcx);
343
344     let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| {
345         debug!("{:#?}", region_constraints);
346         region_constraints.constraints.iter().find_map(|(constraint, cause)| {
347             match *constraint {
348                 Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
349                     Some((sub, cause.clone()))
350                 }
351                 // FIXME: Should this check the universe of the var?
352                 Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
353                     Some((tcx.mk_region(ty::ReVar(vid)), cause.clone()))
354                 }
355                 _ => None,
356             }
357         })
358     })?;
359
360     debug!(?sub_region, "cause = {:#?}", cause);
361     let nice_error = match (error_region, sub_region) {
362         (Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new(
363             infcx,
364             RegionResolutionError::SubSupConflict(
365                 vid,
366                 infcx.region_var_origin(vid),
367                 cause.clone(),
368                 error_region,
369                 cause.clone(),
370                 placeholder_region,
371             ),
372         ),
373         (Some(error_region), _) => NiceRegionError::new(
374             infcx,
375             RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region),
376         ),
377         // Note universe here is wrong...
378         (None, &ty::ReVar(vid)) => NiceRegionError::new(
379             infcx,
380             RegionResolutionError::UpperBoundUniverseConflict(
381                 vid,
382                 infcx.region_var_origin(vid),
383                 infcx.universe_of_region(sub_region),
384                 cause.clone(),
385                 placeholder_region,
386             ),
387         ),
388         (None, _) => NiceRegionError::new(
389             infcx,
390             RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region),
391         ),
392     };
393     nice_error.try_report_from_nll().or_else(|| {
394         if let SubregionOrigin::Subtype(trace) = cause {
395             Some(
396                 infcx.report_and_explain_type_error(*trace, &TypeError::RegionsPlaceholderMismatch),
397             )
398         } else {
399             None
400         }
401     })
402 }