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