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