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