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