handle ascription type op in NLL HRTB diagnostics

Refactors the `type_op_ascribe_user_type` query into a version which
accepts a span, and uses it in the nicer NLL HRTB bound region errors.
This commit is contained in:
Rémy Rakic 2021-08-23 23:31:01 +02:00
parent 52c881ff52
commit 820e2680ec
3 changed files with 73 additions and 21 deletions

View File

@ -9,7 +9,7 @@
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op;
use rustc_trait_selection::traits::{SelectionContext, TraitEngineExt as _};
use rustc_traits::type_op_prove_predicate_with_span;
use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_span};
use std::fmt;
use std::rc::Rc;
@ -104,10 +104,11 @@ fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx
impl<'tcx> ToUniverseInfo<'tcx>
for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>
{
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
// Ascribe user type isn't usually called on types that have different
// bound regions.
UniverseInfo::other()
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(AscribeUserTypeQuery {
canonical_query: self,
base_universe,
})))
}
}
@ -267,6 +268,37 @@ fn nice_error(
}
}
struct AscribeUserTypeQuery<'tcx> {
canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>,
base_universe: ty::UniverseIndex,
}
impl TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'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.sess.struct_span_err(span, "higher-ranked lifetime error")
}
fn base_universe(&self) -> ty::UniverseIndex {
self.base_universe
}
fn nice_error(
&self,
tcx: TyCtxt<'tcx>,
span: Span,
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx>> {
tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(span)).ok()?;
try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
})
}
}
fn try_extract_error_from_fulfill_cx<'tcx>(
mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
infcx: &InferCtxt<'_, 'tcx>,

View File

@ -19,7 +19,7 @@
mod normalize_projection_ty;
mod type_op;
pub use type_op::type_op_prove_predicate_with_span;
pub use type_op::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_span};
use rustc_middle::ty::query::Providers;

View File

@ -40,20 +40,30 @@ fn type_op_ascribe_user_type<'tcx>(
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| {
let (param_env, AscribeUserType { mir_ty, def_id, user_substs }) = key.into_parts();
debug!(
"type_op_ascribe_user_type: mir_ty={:?} def_id={:?} user_substs={:?}",
mir_ty, def_id, user_substs
);
let mut cx = AscribeUserTypeCx { infcx, param_env, fulfill_cx };
cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs)?;
Ok(())
type_op_ascribe_user_type_with_span(infcx, fulfill_cx, key, None)
})
}
/// The core of the `type_op_ascribe_user_type` query: for diagnostics purposes in NLL HRTB errors,
/// this query can be re-run to better track the span of the obligation cause, and improve the error
/// message. Do not call directly unless you're in that very specific context.
pub fn type_op_ascribe_user_type_with_span<'a, 'tcx: 'a>(
infcx: &'a InferCtxt<'a, 'tcx>,
fulfill_cx: &'a mut dyn TraitEngine<'tcx>,
key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>,
span: Option<Span>,
) -> Result<(), NoSolution> {
let (param_env, AscribeUserType { mir_ty, def_id, user_substs }) = key.into_parts();
debug!(
"type_op_ascribe_user_type: mir_ty={:?} def_id={:?} user_substs={:?}",
mir_ty, def_id, user_substs
);
let mut cx = AscribeUserTypeCx { infcx, param_env, fulfill_cx };
cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs, span)?;
Ok(())
}
struct AscribeUserTypeCx<'me, 'tcx> {
infcx: &'me InferCtxt<'me, 'tcx>,
param_env: ParamEnv<'tcx>,
@ -85,10 +95,15 @@ fn relate<T>(&mut self, a: T, variance: Variance, b: T) -> Result<(), NoSolution
Ok(())
}
fn prove_predicate(&mut self, predicate: Predicate<'tcx>) {
fn prove_predicate(&mut self, predicate: Predicate<'tcx>, span: Option<Span>) {
let cause = if let Some(span) = span {
ObligationCause::dummy_with_span(span)
} else {
ObligationCause::dummy()
};
self.fulfill_cx.register_predicate_obligation(
self.infcx,
Obligation::new(ObligationCause::dummy(), self.param_env, predicate),
Obligation::new(cause, self.param_env, predicate),
);
}
@ -108,6 +123,7 @@ fn relate_mir_and_user_ty(
mir_ty: Ty<'tcx>,
def_id: DefId,
user_substs: UserSubsts<'tcx>,
span: Option<Span>,
) -> Result<(), NoSolution> {
let UserSubsts { user_self_ty, substs } = user_substs;
let tcx = self.tcx();
@ -129,7 +145,7 @@ fn relate_mir_and_user_ty(
debug!(?instantiated_predicates.predicates);
for instantiated_predicate in instantiated_predicates.predicates {
let instantiated_predicate = self.normalize(instantiated_predicate);
self.prove_predicate(instantiated_predicate);
self.prove_predicate(instantiated_predicate, span);
}
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
@ -141,6 +157,7 @@ fn relate_mir_and_user_ty(
self.prove_predicate(
ty::PredicateKind::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()),
span,
);
}
@ -155,7 +172,10 @@ fn relate_mir_and_user_ty(
// them? This would only be relevant if some input
// type were ill-formed but did not appear in `ty`,
// which...could happen with normalization...
self.prove_predicate(ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()));
self.prove_predicate(
ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()),
span,
);
Ok(())
}
}