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