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