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, }; 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::region_infer::values::RegionElement; 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 + '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 } } pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, error_element: RegionElement, 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> + '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> + '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>, ) -> Option>; /// 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, error_element: RegionElement, 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::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound }, ); let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) = error_element { let adjusted_universe = error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32()); adjusted_universe.map(|adjusted| { ty::Region::new_placeholder( tcx, ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound }, ) }) } else { None }; 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>, ) -> Option> { 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> + '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>, ) -> Option> { 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, 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> + '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>, ) -> Option> { 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, 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>, ) -> Option> { 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>, ) -> Option> { 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>, ) -> Option> { // 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.select_all_or_error(); 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>, region_constraints: &RegionConstraintData<'tcx>, mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin, mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex, ) -> Option> { 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())) } _ => None, }; let mut find_culprit = |exact_match: bool| { region_constraints .constraints .iter() .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 } }) }