Skip to content

Commit

Permalink
Rollup merge of rust-lang#88270 - lqd:hrtb-type-ascription, r=nikomat…
Browse files Browse the repository at this point in the history
…sakis

Handle type ascription type ops in NLL HRTB diagnostics

Currently, there are still a few cases of the "higher-ranked subtype error" of yore, 4 of which are related to type ascription.

This PR is a follow-up to rust-lang#86700, adding support for type ascription type ops, and makes 3 of these tests output the same diagnostics in NLL mode as the migrate mode (and 1 is now much closer, especially if you ignore that it already outputs an additional error in NLL mode -- which could be a duplicate caused by a lack of normalization like [these comments point out](https://github.com/rust-lang/rust/blob/9583fd1bdd0127328e25e5b8c24dff575ec2c86b/compiler/rustc_traits/src/type_op.rs#L122-L157), or an imprecision in some parts of normalization as [described here](rust-lang#86700 (comment))).

Since we discussed these recently:
- [here](rust-lang#86700 (comment)), cc `@matthewjasper,`
- and [here](rust-lang#57374 (comment)), cc `@Aaron1011.`

It should only leave [this TAIT test](https://github.com/rust-lang/rust/blob/9583fd1bdd0127328e25e5b8c24dff575ec2c86b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs) as still emitting [the terse error](https://github.com/rust-lang/rust/blob/9583fd1bdd0127328e25e5b8c24dff575ec2c86b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr).

r? `@estebank` (so that they shake their fist at NLL's general direction less often) or `@nikomatsakis` or matthew or aaron, the more the merrier.
  • Loading branch information
Dylan-DPC committed Aug 26, 2021
2 parents 59cd68d + 7b0e564 commit 1d79194
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
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;
Expand Down Expand Up @@ -104,10 +104,11 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx> ToUniverseInfo<'t
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,
})))
}
}

Expand Down Expand Up @@ -267,6 +268,37 @@ where
}
}

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>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mod normalize_erasing_regions;
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;

Expand Down
48 changes: 34 additions & 14 deletions compiler/rustc_traits/src/type_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,28 @@ 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
);
type_op_ascribe_user_type_with_span(infcx, fulfill_cx, key, None)
})
}

let mut cx = AscribeUserTypeCx { infcx, param_env, fulfill_cx };
cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs)?;
/// 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
);

Ok(())
})
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> {
Expand Down Expand Up @@ -85,10 +95,15 @@ impl AscribeUserTypeCx<'me, 'tcx> {
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),
);
}

Expand All @@ -108,6 +123,7 @@ impl AscribeUserTypeCx<'me, 'tcx> {
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();
Expand All @@ -129,7 +145,7 @@ impl AscribeUserTypeCx<'me, 'tcx> {
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 {
Expand All @@ -141,6 +157,7 @@ impl AscribeUserTypeCx<'me, 'tcx> {

self.prove_predicate(
ty::PredicateKind::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()),
span,
);
}

Expand All @@ -155,7 +172,10 @@ impl AscribeUserTypeCx<'me, 'tcx> {
// 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(())
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/test/ui/hrtb/due-to-where-clause.nll.stderr

This file was deleted.

8 changes: 0 additions & 8 deletions src/test/ui/hrtb/hrtb-cache-issue-54302.nll.stderr

This file was deleted.

7 changes: 5 additions & 2 deletions src/test/ui/hrtb/hrtb-just-for-static.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ LL | want_hrtb::<&'a u32>()
|
= help: consider replacing `'a` with `'static`

error: higher-ranked subtype error
error: implementation of `Foo` is not general enough
--> $DIR/hrtb-just-for-static.rs:30:5
|
LL | want_hrtb::<&'a u32>()
| ^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
= note: `Foo<&'0 isize>` would have to be implemented for the type `&u32`, for any lifetime `'0`...
= note: ...but `Foo<&'1 isize>` is actually implemented for the type `&'1 u32`, for some specific lifetime `'1`

error: aborting due to 3 previous errors

8 changes: 0 additions & 8 deletions src/test/ui/issues/issue-54302.nll.stderr

This file was deleted.

0 comments on commit 1d79194

Please sign in to comment.