rustc_borrowck/diagnostics/
bound_region_errors.rs

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