| use std::fmt; | |
| use std::rc::Rc; | |
| use rustc_errors::Diag; | |
| use rustc_hir::def_id::LocalDefId; | |
| use rustc_infer::infer::region_constraints::{Constraint, ConstraintKind, RegionConstraintData}; | |
| use rustc_infer::infer::{ | |
| InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt as _, | |
| }; | |
| use rustc_infer::traits::ObligationCause; | |
| use rustc_infer::traits::query::{ | |
| CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpDeeplyNormalizeGoal, | |
| CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, | |
| }; | |
| use rustc_middle::ty::error::TypeError; | |
| use rustc_middle::ty::{ | |
| self, RePlaceholder, Region, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, Unnormalized, | |
| }; | |
| use rustc_span::Span; | |
| use rustc_trait_selection::error_reporting::InferCtxtErrorExt; | |
| use rustc_trait_selection::error_reporting::infer::nice_region_error::NiceRegionError; | |
| use rustc_trait_selection::traits::ObligationCtxt; | |
| use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause}; | |
| use tracing::{debug, instrument}; | |
| use crate::MirBorrowckCtxt; | |
| use crate::session_diagnostics::{ | |
| HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError, | |
| }; | |
| /// What operation a universe was created for. | |
| #[derive(Clone)] | |
| pub(crate) enum UniverseInfo<'tcx> { | |
| /// Relating two types which have binders. | |
| RelateTys { expected: Ty<'tcx>, found: Ty<'tcx> }, | |
| /// Created from performing a `TypeOp`. | |
| TypeOp(Rc<dyn TypeOpInfo<'tcx> + 'tcx>), | |
| /// Any other reason. | |
| Other, | |
| } | |
| impl<'tcx> UniverseInfo<'tcx> { | |
| pub(crate) fn other() -> UniverseInfo<'tcx> { | |
| UniverseInfo::Other | |
| } | |
| pub(crate) fn relate(expected: Ty<'tcx>, found: Ty<'tcx>) -> UniverseInfo<'tcx> { | |
| UniverseInfo::RelateTys { expected, found } | |
| } | |
| /// Report an error where an element erroneously made its way into `placeholder`. | |
| pub(crate) fn report_erroneous_element( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, | |
| placeholder: ty::PlaceholderRegion<'tcx>, | |
| error_element: Option<ty::PlaceholderRegion<'tcx>>, | |
| cause: ObligationCause<'tcx>, | |
| ) { | |
| match *self { | |
| UniverseInfo::RelateTys { expected, found } => { | |
| let err = mbcx.infcx.err_ctxt().report_mismatched_types( | |
| &cause, | |
| mbcx.infcx.param_env, | |
| expected, | |
| found, | |
| TypeError::RegionsPlaceholderMismatch, | |
| ); | |
| mbcx.buffer_error(err); | |
| } | |
| UniverseInfo::TypeOp(ref type_op_info) => { | |
| type_op_info.report_erroneous_element(mbcx, placeholder, error_element, cause); | |
| } | |
| UniverseInfo::Other => { | |
| // FIXME: This error message isn't great, but it doesn't show | |
| // up in the existing UI tests. Consider investigating this | |
| // some more. | |
| mbcx.buffer_error( | |
| mbcx.dcx().create_err(HigherRankedSubtypeError { span: cause.span }), | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) trait ToUniverseInfo<'tcx> { | |
| fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>; | |
| } | |
| impl<'tcx> ToUniverseInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { | |
| fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { | |
| UniverseInfo::TypeOp(Rc::new(crate::type_check::InstantiateOpaqueType { | |
| base_universe: Some(base_universe), | |
| ..self | |
| })) | |
| } | |
| } | |
| impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpProvePredicateGoal<'tcx> { | |
| fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { | |
| UniverseInfo::TypeOp(Rc::new(PredicateQuery { canonical_query: self, base_universe })) | |
| } | |
| } | |
| impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUniverseInfo<'tcx> | |
| for CanonicalTypeOpNormalizeGoal<'tcx, T> | |
| { | |
| fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { | |
| UniverseInfo::TypeOp(Rc::new(NormalizeQuery { canonical_query: self, base_universe })) | |
| } | |
| } | |
| impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUniverseInfo<'tcx> | |
| for CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T> | |
| { | |
| fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { | |
| UniverseInfo::TypeOp(Rc::new(DeeplyNormalizeQuery { canonical_query: self, base_universe })) | |
| } | |
| } | |
| impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpAscribeUserTypeGoal<'tcx> { | |
| fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { | |
| UniverseInfo::TypeOp(Rc::new(AscribeUserTypeQuery { canonical_query: self, base_universe })) | |
| } | |
| } | |
| impl<'tcx> ToUniverseInfo<'tcx> for ! { | |
| fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { | |
| self | |
| } | |
| } | |
| #[allow(unused_lifetimes)] | |
| pub(crate) trait TypeOpInfo<'tcx> { | |
| /// Returns an error to be reported if rerunning the type op fails to | |
| /// recover the error's cause. | |
| fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx>; | |
| fn base_universe(&self) -> ty::UniverseIndex; | |
| fn nice_error<'infcx>( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'infcx>>; | |
| /// Constraints require that `error_element` appear in the | |
| /// values of `placeholder`, but this cannot be proven to | |
| /// hold. Report an error. | |
| #[instrument(level = "debug", skip(self, mbcx))] | |
| fn report_erroneous_element( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, | |
| placeholder: ty::PlaceholderRegion<'tcx>, | |
| error_element: Option<ty::PlaceholderRegion<'tcx>>, | |
| cause: ObligationCause<'tcx>, | |
| ) { | |
| let tcx = mbcx.infcx.tcx; | |
| let base_universe = self.base_universe(); | |
| debug!(?base_universe); | |
| let Some(adjusted_universe) = | |
| placeholder.universe.as_u32().checked_sub(base_universe.as_u32()) | |
| else { | |
| mbcx.buffer_error(self.fallback_error(tcx, cause.span)); | |
| return; | |
| }; | |
| let placeholder_region = ty::Region::new_placeholder( | |
| tcx, | |
| ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound), | |
| ); | |
| // FIXME: one day this should just be error_element, | |
| // and this method shouldn't do anything. | |
| let error_region = error_element.and_then(|e| { | |
| let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32()); | |
| adjusted_universe.map(|adjusted| { | |
| ty::Region::new_placeholder( | |
| tcx, | |
| ty::PlaceholderRegion::new(adjusted.into(), e.bound), | |
| ) | |
| }) | |
| }); | |
| debug!(?placeholder_region); | |
| let span = cause.span; | |
| let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region); | |
| debug!(?nice_error); | |
| mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span))); | |
| } | |
| } | |
| struct PredicateQuery<'tcx> { | |
| canonical_query: CanonicalTypeOpProvePredicateGoal<'tcx>, | |
| base_universe: ty::UniverseIndex, | |
| } | |
| impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { | |
| fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { | |
| tcx.dcx().create_err(HigherRankedLifetimeError { | |
| cause: Some(HigherRankedErrorCause::CouldNotProve { | |
| predicate: self.canonical_query.canonical.value.value.predicate.to_string(), | |
| }), | |
| span, | |
| }) | |
| } | |
| fn base_universe(&self) -> ty::UniverseIndex { | |
| self.base_universe | |
| } | |
| fn nice_error<'infcx>( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'infcx>> { | |
| let (infcx, key, _) = | |
| mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); | |
| let ocx = ObligationCtxt::new(&infcx); | |
| type_op_prove_predicate_with_cause(&ocx, key, cause); | |
| let diag = try_extract_error_from_fulfill_cx( | |
| &ocx, | |
| mbcx.mir_def_id(), | |
| placeholder_region, | |
| error_region, | |
| )? | |
| .with_dcx(mbcx.dcx()); | |
| Some(diag) | |
| } | |
| } | |
| struct NormalizeQuery<'tcx, T> { | |
| canonical_query: CanonicalTypeOpNormalizeGoal<'tcx, T>, | |
| base_universe: ty::UniverseIndex, | |
| } | |
| impl<'tcx, T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T> | |
| where | |
| T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx, | |
| { | |
| fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { | |
| tcx.dcx().create_err(HigherRankedLifetimeError { | |
| cause: Some(HigherRankedErrorCause::CouldNotNormalize { | |
| value: self.canonical_query.canonical.value.value.value.to_string(), | |
| }), | |
| span, | |
| }) | |
| } | |
| fn base_universe(&self) -> ty::UniverseIndex { | |
| self.base_universe | |
| } | |
| fn nice_error<'infcx>( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'infcx>> { | |
| let (infcx, key, _) = | |
| mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); | |
| let ocx = ObligationCtxt::new(&infcx); | |
| // FIXME(lqd): Unify and de-duplicate the following with the actual | |
| // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the | |
| // `ObligationCause`. The normalization results are currently different between | |
| // `QueryNormalizeExt::query_normalize` used in the query and `normalize` called below: | |
| // the former fails to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` | |
| // test. Check after #85499 lands to see if its fixes have erased this difference. | |
| let ty::ParamEnvAnd { param_env, value } = key; | |
| let _ = ocx.normalize(&cause, param_env, Unnormalized::new_wip(value.value)); | |
| let diag = try_extract_error_from_fulfill_cx( | |
| &ocx, | |
| mbcx.mir_def_id(), | |
| placeholder_region, | |
| error_region, | |
| )? | |
| .with_dcx(mbcx.dcx()); | |
| Some(diag) | |
| } | |
| } | |
| struct DeeplyNormalizeQuery<'tcx, T> { | |
| canonical_query: CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>, | |
| base_universe: ty::UniverseIndex, | |
| } | |
| impl<'tcx, T> TypeOpInfo<'tcx> for DeeplyNormalizeQuery<'tcx, T> | |
| where | |
| T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx, | |
| { | |
| fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { | |
| tcx.dcx().create_err(HigherRankedLifetimeError { | |
| cause: Some(HigherRankedErrorCause::CouldNotNormalize { | |
| value: self.canonical_query.canonical.value.value.value.to_string(), | |
| }), | |
| span, | |
| }) | |
| } | |
| fn base_universe(&self) -> ty::UniverseIndex { | |
| self.base_universe | |
| } | |
| fn nice_error<'infcx>( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'infcx>> { | |
| let (infcx, key, _) = | |
| mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); | |
| let ocx = ObligationCtxt::new(&infcx); | |
| let ty::ParamEnvAnd { param_env, value } = key; | |
| let _ = ocx.deeply_normalize(&cause, param_env, Unnormalized::new_wip(value.value)); | |
| let diag = try_extract_error_from_fulfill_cx( | |
| &ocx, | |
| mbcx.mir_def_id(), | |
| placeholder_region, | |
| error_region, | |
| )? | |
| .with_dcx(mbcx.dcx()); | |
| Some(diag) | |
| } | |
| } | |
| struct AscribeUserTypeQuery<'tcx> { | |
| canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>, | |
| base_universe: ty::UniverseIndex, | |
| } | |
| impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { | |
| fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { | |
| // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests, | |
| // and is only the fallback when the nice error fails. Consider improving this some more. | |
| tcx.dcx().create_err(HigherRankedLifetimeError { cause: None, span }) | |
| } | |
| fn base_universe(&self) -> ty::UniverseIndex { | |
| self.base_universe | |
| } | |
| fn nice_error<'infcx>( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'infcx>> { | |
| let (infcx, key, _) = | |
| mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); | |
| let ocx = ObligationCtxt::new(&infcx); | |
| type_op_ascribe_user_type_with_span(&ocx, key, cause.span).ok()?; | |
| let diag = try_extract_error_from_fulfill_cx( | |
| &ocx, | |
| mbcx.mir_def_id(), | |
| placeholder_region, | |
| error_region, | |
| )? | |
| .with_dcx(mbcx.dcx()); | |
| Some(diag) | |
| } | |
| } | |
| impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { | |
| fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { | |
| // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests, | |
| // and is only the fallback when the nice error fails. Consider improving this some more. | |
| tcx.dcx().create_err(HigherRankedLifetimeError { cause: None, span }) | |
| } | |
| fn base_universe(&self) -> ty::UniverseIndex { | |
| self.base_universe.unwrap() | |
| } | |
| fn nice_error<'infcx>( | |
| &self, | |
| mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, | |
| _cause: ObligationCause<'tcx>, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'infcx>> { | |
| try_extract_error_from_region_constraints( | |
| mbcx.infcx, | |
| mbcx.mir_def_id(), | |
| placeholder_region, | |
| error_region, | |
| self.region_constraints.as_ref().unwrap(), | |
| // We're using the original `InferCtxt` that we | |
| // started MIR borrowchecking with, so the region | |
| // constraints have already been taken. Use the data from | |
| // our `mbcx` instead. | |
| |vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin), | |
| |vid| mbcx.regioncx.definitions[vid].universe, | |
| ) | |
| } | |
| } | |
| #[instrument(skip(ocx), level = "debug")] | |
| fn try_extract_error_from_fulfill_cx<'a, 'tcx>( | |
| ocx: &ObligationCtxt<'a, 'tcx>, | |
| generic_param_scope: LocalDefId, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| ) -> Option<Diag<'a>> { | |
| // We generally shouldn't have errors here because the query was | |
| // already run, but there's no point using `span_delayed_bug` | |
| // when we're going to emit an error here anyway. | |
| let _errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone()); | |
| try_extract_error_from_region_constraints( | |
| ocx.infcx, | |
| generic_param_scope, | |
| placeholder_region, | |
| error_region, | |
| ®ion_constraints, | |
| |vid| ocx.infcx.region_var_origin(vid), | |
| |vid| ocx.infcx.universe_of_region(ty::Region::new_var(ocx.infcx.tcx, vid)), | |
| ) | |
| } | |
| #[instrument(level = "debug", skip(infcx, region_var_origin, universe_of_region))] | |
| fn try_extract_error_from_region_constraints<'a, 'tcx>( | |
| infcx: &'a InferCtxt<'tcx>, | |
| generic_param_scope: LocalDefId, | |
| placeholder_region: ty::Region<'tcx>, | |
| error_region: Option<ty::Region<'tcx>>, | |
| region_constraints: &RegionConstraintData<'tcx>, | |
| mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin<'tcx>, | |
| mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex, | |
| ) -> Option<Diag<'a>> { | |
| let placeholder_universe = match placeholder_region.kind() { | |
| ty::RePlaceholder(p) => p.universe, | |
| ty::ReVar(vid) => universe_of_region(vid), | |
| _ => ty::UniverseIndex::ROOT, | |
| }; | |
| // Are the two regions the same? | |
| let regions_the_same = | |
| |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { | |
| (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, | |
| _ => a_region == b_region, | |
| }; | |
| let mut check = |c: Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match c.kind { | |
| ConstraintKind::RegSubReg | |
| if ((exact && c.sup == placeholder_region) | |
| || (!exact && regions_the_same(c.sup, placeholder_region))) | |
| && c.sup != c.sub => | |
| { | |
| Some((c.sub, cause.clone())) | |
| } | |
| ConstraintKind::VarSubReg | |
| if (exact | |
| && c.sup == placeholder_region | |
| && !universe_of_region(c.sub.as_var()).can_name(placeholder_universe)) | |
| || (!exact && regions_the_same(c.sup, placeholder_region)) => | |
| { | |
| Some((c.sub, cause.clone())) | |
| } | |
| ConstraintKind::VarSubVar | |
| | ConstraintKind::RegSubVar | |
| | ConstraintKind::VarSubReg | |
| | ConstraintKind::RegSubReg => None, | |
| ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => { | |
| unreachable!() | |
| } | |
| }; | |
| let mut find_culprit = |exact_match: bool| { | |
| region_constraints | |
| .constraints | |
| .iter() | |
| .flat_map(|(constraint, cause)| { | |
| constraint.iter_outlives().map(move |constraint| (constraint, cause)) | |
| }) | |
| .find_map(|(constraint, cause)| check(constraint, cause, exact_match)) | |
| }; | |
| let (sub_region, cause) = find_culprit(true).or_else(|| find_culprit(false))?; | |
| debug!(?sub_region, "cause = {:#?}", cause); | |
| let error = match (error_region, sub_region.kind()) { | |
| (Some(error_region), ty::ReVar(vid)) => RegionResolutionError::SubSupConflict( | |
| vid, | |
| region_var_origin(vid), | |
| cause.clone(), | |
| error_region, | |
| cause.clone(), | |
| placeholder_region, | |
| vec![], | |
| ), | |
| (Some(error_region), _) => { | |
| RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region) | |
| } | |
| // Note universe here is wrong... | |
| (None, ty::ReVar(vid)) => RegionResolutionError::UpperBoundUniverseConflict( | |
| vid, | |
| region_var_origin(vid), | |
| universe_of_region(vid), | |
| cause.clone(), | |
| placeholder_region, | |
| ), | |
| (None, _) => { | |
| RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region) | |
| } | |
| }; | |
| NiceRegionError::new(&infcx.err_ctxt(), generic_param_scope, error) | |
| .try_report_from_nll() | |
| .or_else(|| { | |
| if let SubregionOrigin::Subtype(trace) = cause { | |
| Some(infcx.err_ctxt().report_and_explain_type_error( | |
| *trace, | |
| infcx.tcx.param_env(generic_param_scope), | |
| TypeError::RegionsPlaceholderMismatch, | |
| )) | |
| } else { | |
| None | |
| } | |
| }) | |
| } |