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, ConstraintKind, 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_erroneous_element(
53        &self,
54        mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
55        placeholder: ty::PlaceholderRegion<'tcx>,
56        error_element: RegionElement<'tcx>,
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_erroneous_element(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    /// Constraints require that `error_element` appear in the
149    ///  values of `placeholder`, but this cannot be proven to
150    /// hold. Report an error.
151    #[instrument(level = "debug", skip(self, mbcx))]
152    fn report_erroneous_element(
153        &self,
154        mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
155        placeholder: ty::PlaceholderRegion<'tcx>,
156        error_element: RegionElement<'tcx>,
157        cause: ObligationCause<'tcx>,
158    ) {
159        let tcx = mbcx.infcx.tcx;
160        let base_universe = self.base_universe();
161        debug!(?base_universe);
162
163        let Some(adjusted_universe) =
164            placeholder.universe.as_u32().checked_sub(base_universe.as_u32())
165        else {
166            mbcx.buffer_error(self.fallback_error(tcx, cause.span));
167            return;
168        };
169
170        let placeholder_region = ty::Region::new_placeholder(
171            tcx,
172            ty::Placeholder::new(adjusted_universe.into(), placeholder.bound),
173        );
174
175        let error_region =
176            if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
177                let adjusted_universe =
178                    error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
179                adjusted_universe.map(|adjusted| {
180                    ty::Region::new_placeholder(
181                        tcx,
182                        ty::Placeholder::new(adjusted.into(), error_placeholder.bound),
183                    )
184                })
185            } else {
186                None
187            };
188
189        debug!(?placeholder_region);
190
191        let span = cause.span;
192        let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
193
194        debug!(?nice_error);
195        mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span)));
196    }
197}
198
199struct PredicateQuery<'tcx> {
200    canonical_query: CanonicalTypeOpProvePredicateGoal<'tcx>,
201    base_universe: ty::UniverseIndex,
202}
203
204impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
205    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
206        tcx.dcx().create_err(HigherRankedLifetimeError {
207            cause: Some(HigherRankedErrorCause::CouldNotProve {
208                predicate: self.canonical_query.canonical.value.value.predicate.to_string(),
209            }),
210            span,
211        })
212    }
213
214    fn base_universe(&self) -> ty::UniverseIndex {
215        self.base_universe
216    }
217
218    fn nice_error<'infcx>(
219        &self,
220        mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
221        cause: ObligationCause<'tcx>,
222        placeholder_region: ty::Region<'tcx>,
223        error_region: Option<ty::Region<'tcx>>,
224    ) -> Option<Diag<'infcx>> {
225        let (infcx, key, _) =
226            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
227        let ocx = ObligationCtxt::new(&infcx);
228        type_op_prove_predicate_with_cause(&ocx, key, cause);
229        let diag = try_extract_error_from_fulfill_cx(
230            &ocx,
231            mbcx.mir_def_id(),
232            placeholder_region,
233            error_region,
234        )?
235        .with_dcx(mbcx.dcx());
236        Some(diag)
237    }
238}
239
240struct NormalizeQuery<'tcx, T> {
241    canonical_query: CanonicalTypeOpNormalizeGoal<'tcx, T>,
242    base_universe: ty::UniverseIndex,
243}
244
245impl<'tcx, T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T>
246where
247    T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx,
248{
249    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
250        tcx.dcx().create_err(HigherRankedLifetimeError {
251            cause: Some(HigherRankedErrorCause::CouldNotNormalize {
252                value: self.canonical_query.canonical.value.value.value.to_string(),
253            }),
254            span,
255        })
256    }
257
258    fn base_universe(&self) -> ty::UniverseIndex {
259        self.base_universe
260    }
261
262    fn nice_error<'infcx>(
263        &self,
264        mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
265        cause: ObligationCause<'tcx>,
266        placeholder_region: ty::Region<'tcx>,
267        error_region: Option<ty::Region<'tcx>>,
268    ) -> Option<Diag<'infcx>> {
269        let (infcx, key, _) =
270            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
271        let ocx = ObligationCtxt::new(&infcx);
272
273        // FIXME(lqd): Unify and de-duplicate the following with the actual
274        // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
275        // `ObligationCause`. The normalization results are currently different between
276        // `QueryNormalizeExt::query_normalize` used in the query and `normalize` called below:
277        // the former fails to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs`
278        // test. Check after #85499 lands to see if its fixes have erased this difference.
279        let ty::ParamEnvAnd { param_env, value } = key;
280        let _ = ocx.normalize(&cause, param_env, value.value);
281
282        let diag = try_extract_error_from_fulfill_cx(
283            &ocx,
284            mbcx.mir_def_id(),
285            placeholder_region,
286            error_region,
287        )?
288        .with_dcx(mbcx.dcx());
289        Some(diag)
290    }
291}
292
293struct DeeplyNormalizeQuery<'tcx, T> {
294    canonical_query: CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>,
295    base_universe: ty::UniverseIndex,
296}
297
298impl<'tcx, T> TypeOpInfo<'tcx> for DeeplyNormalizeQuery<'tcx, T>
299where
300    T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx,
301{
302    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
303        tcx.dcx().create_err(HigherRankedLifetimeError {
304            cause: Some(HigherRankedErrorCause::CouldNotNormalize {
305                value: self.canonical_query.canonical.value.value.value.to_string(),
306            }),
307            span,
308        })
309    }
310
311    fn base_universe(&self) -> ty::UniverseIndex {
312        self.base_universe
313    }
314
315    fn nice_error<'infcx>(
316        &self,
317        mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
318        cause: ObligationCause<'tcx>,
319        placeholder_region: ty::Region<'tcx>,
320        error_region: Option<ty::Region<'tcx>>,
321    ) -> Option<Diag<'infcx>> {
322        let (infcx, key, _) =
323            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
324        let ocx = ObligationCtxt::new(&infcx);
325
326        let ty::ParamEnvAnd { param_env, value } = key;
327        let _ = ocx.deeply_normalize(&cause, param_env, value.value);
328
329        let diag = try_extract_error_from_fulfill_cx(
330            &ocx,
331            mbcx.mir_def_id(),
332            placeholder_region,
333            error_region,
334        )?
335        .with_dcx(mbcx.dcx());
336        Some(diag)
337    }
338}
339
340struct AscribeUserTypeQuery<'tcx> {
341    canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>,
342    base_universe: ty::UniverseIndex,
343}
344
345impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
346    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
347        // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
348        // and is only the fallback when the nice error fails. Consider improving this some more.
349        tcx.dcx().create_err(HigherRankedLifetimeError { cause: None, span })
350    }
351
352    fn base_universe(&self) -> ty::UniverseIndex {
353        self.base_universe
354    }
355
356    fn nice_error<'infcx>(
357        &self,
358        mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
359        cause: ObligationCause<'tcx>,
360        placeholder_region: ty::Region<'tcx>,
361        error_region: Option<ty::Region<'tcx>>,
362    ) -> Option<Diag<'infcx>> {
363        let (infcx, key, _) =
364            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
365        let ocx = ObligationCtxt::new(&infcx);
366        type_op_ascribe_user_type_with_span(&ocx, key, cause.span).ok()?;
367        let diag = try_extract_error_from_fulfill_cx(
368            &ocx,
369            mbcx.mir_def_id(),
370            placeholder_region,
371            error_region,
372        )?
373        .with_dcx(mbcx.dcx());
374        Some(diag)
375    }
376}
377
378impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
379    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
380        // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
381        // and is only the fallback when the nice error fails. Consider improving this some more.
382        tcx.dcx().create_err(HigherRankedLifetimeError { cause: None, span })
383    }
384
385    fn base_universe(&self) -> ty::UniverseIndex {
386        self.base_universe.unwrap()
387    }
388
389    fn nice_error<'infcx>(
390        &self,
391        mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
392        _cause: ObligationCause<'tcx>,
393        placeholder_region: ty::Region<'tcx>,
394        error_region: Option<ty::Region<'tcx>>,
395    ) -> Option<Diag<'infcx>> {
396        try_extract_error_from_region_constraints(
397            mbcx.infcx,
398            mbcx.mir_def_id(),
399            placeholder_region,
400            error_region,
401            self.region_constraints.as_ref().unwrap(),
402            // We're using the original `InferCtxt` that we
403            // started MIR borrowchecking with, so the region
404            // constraints have already been taken. Use the data from
405            // our `mbcx` instead.
406            |vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin),
407            |vid| mbcx.regioncx.definitions[vid].universe,
408        )
409    }
410}
411
412#[instrument(skip(ocx), level = "debug")]
413fn try_extract_error_from_fulfill_cx<'a, 'tcx>(
414    ocx: &ObligationCtxt<'a, 'tcx>,
415    generic_param_scope: LocalDefId,
416    placeholder_region: ty::Region<'tcx>,
417    error_region: Option<ty::Region<'tcx>>,
418) -> Option<Diag<'a>> {
419    // We generally shouldn't have errors here because the query was
420    // already run, but there's no point using `span_delayed_bug`
421    // when we're going to emit an error here anyway.
422    let _errors = ocx.evaluate_obligations_error_on_ambiguity();
423    let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone());
424    try_extract_error_from_region_constraints(
425        ocx.infcx,
426        generic_param_scope,
427        placeholder_region,
428        error_region,
429        &region_constraints,
430        |vid| ocx.infcx.region_var_origin(vid),
431        |vid| ocx.infcx.universe_of_region(ty::Region::new_var(ocx.infcx.tcx, vid)),
432    )
433}
434
435#[instrument(level = "debug", skip(infcx, region_var_origin, universe_of_region))]
436fn try_extract_error_from_region_constraints<'a, 'tcx>(
437    infcx: &'a InferCtxt<'tcx>,
438    generic_param_scope: LocalDefId,
439    placeholder_region: ty::Region<'tcx>,
440    error_region: Option<ty::Region<'tcx>>,
441    region_constraints: &RegionConstraintData<'tcx>,
442    mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin<'tcx>,
443    mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
444) -> Option<Diag<'a>> {
445    let placeholder_universe = match placeholder_region.kind() {
446        ty::RePlaceholder(p) => p.universe,
447        ty::ReVar(vid) => universe_of_region(vid),
448        _ => ty::UniverseIndex::ROOT,
449    };
450    // Are the two regions the same?
451    let regions_the_same =
452        |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) {
453            (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound,
454            _ => a_region == b_region,
455        };
456    let mut check = |c: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match c.kind {
457        ConstraintKind::RegSubReg
458            if ((exact && c.sup == placeholder_region)
459                || (!exact && regions_the_same(c.sup, placeholder_region)))
460                && c.sup != c.sub =>
461        {
462            Some((c.sub, cause.clone()))
463        }
464        ConstraintKind::VarSubReg
465            if (exact
466                && c.sup == placeholder_region
467                && !universe_of_region(c.sub.as_var()).can_name(placeholder_universe))
468                || (!exact && regions_the_same(c.sup, placeholder_region)) =>
469        {
470            Some((c.sub, cause.clone()))
471        }
472        _ => None,
473    };
474
475    let mut find_culprit = |exact_match: bool| {
476        region_constraints
477            .constraints
478            .iter()
479            .find_map(|(constraint, cause)| check(constraint, cause, exact_match))
480    };
481
482    let (sub_region, cause) = find_culprit(true).or_else(|| find_culprit(false))?;
483
484    debug!(?sub_region, "cause = {:#?}", cause);
485    let error = match (error_region, sub_region.kind()) {
486        (Some(error_region), ty::ReVar(vid)) => RegionResolutionError::SubSupConflict(
487            vid,
488            region_var_origin(vid),
489            cause.clone(),
490            error_region,
491            cause.clone(),
492            placeholder_region,
493            vec![],
494        ),
495        (Some(error_region), _) => {
496            RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region)
497        }
498        // Note universe here is wrong...
499        (None, ty::ReVar(vid)) => RegionResolutionError::UpperBoundUniverseConflict(
500            vid,
501            region_var_origin(vid),
502            universe_of_region(vid),
503            cause.clone(),
504            placeholder_region,
505        ),
506        (None, _) => {
507            RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region)
508        }
509    };
510    NiceRegionError::new(&infcx.err_ctxt(), generic_param_scope, error)
511        .try_report_from_nll()
512        .or_else(|| {
513            if let SubregionOrigin::Subtype(trace) = cause {
514                Some(infcx.err_ctxt().report_and_explain_type_error(
515                    *trace,
516                    infcx.tcx.param_env(generic_param_scope),
517                    TypeError::RegionsPlaceholderMismatch,
518                ))
519            } else {
520                None
521            }
522        })
523}