improve infer var handling for implied bounds
This commit is contained in:
parent
efa717bc2d
commit
71f8fd5c58
4 changed files with 41 additions and 34 deletions
|
@ -53,6 +53,7 @@ pub struct OutlivesEnvironment<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder of OutlivesEnvironment.
|
/// Builder of OutlivesEnvironment.
|
||||||
|
#[derive(Debug)]
|
||||||
struct OutlivesEnvironmentBuilder<'tcx> {
|
struct OutlivesEnvironmentBuilder<'tcx> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
region_relation: TransitiveRelationBuilder<Region<'tcx>>,
|
region_relation: TransitiveRelationBuilder<Region<'tcx>>,
|
||||||
|
@ -109,6 +110,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||||
|
|
||||||
impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> {
|
impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[instrument(level = "debug")]
|
||||||
fn build(self) -> OutlivesEnvironment<'tcx> {
|
fn build(self) -> OutlivesEnvironment<'tcx> {
|
||||||
OutlivesEnvironment {
|
OutlivesEnvironment {
|
||||||
param_env: self.param_env,
|
param_env: self.param_env,
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> {
|
||||||
/// Note that this may cause outlives obligations to be injected
|
/// Note that this may cause outlives obligations to be injected
|
||||||
/// into the inference context with this body-id.
|
/// into the inference context with this body-id.
|
||||||
/// - `ty`, the type that we are supposed to assume is WF.
|
/// - `ty`, the type that we are supposed to assume is WF.
|
||||||
#[instrument(level = "debug", skip(self, param_env, body_id))]
|
#[instrument(level = "debug", skip(self, param_env, body_id), ret)]
|
||||||
fn implied_outlives_bounds(
|
fn implied_outlives_bounds(
|
||||||
&self,
|
&self,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
@ -71,6 +71,7 @@ impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> {
|
||||||
let TypeOpOutput { output, constraints, .. } = result;
|
let TypeOpOutput { output, constraints, .. } = result;
|
||||||
|
|
||||||
if let Some(constraints) = constraints {
|
if let Some(constraints) = constraints {
|
||||||
|
debug!(?constraints);
|
||||||
// Instantiation may have produced new inference variables and constraints on those
|
// Instantiation may have produced new inference variables and constraints on those
|
||||||
// variables. Process these constraints.
|
// variables. Process these constraints.
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx);
|
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx);
|
||||||
|
|
|
@ -49,7 +49,8 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
|
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
|
||||||
let mut wf_args = vec![ty.into()];
|
let mut wf_args = vec![ty.into()];
|
||||||
|
|
||||||
let mut implied_bounds = vec![];
|
let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
|
||||||
|
vec![];
|
||||||
|
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
|
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
|
||||||
|
|
||||||
|
@ -65,30 +66,17 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
// than the ultimate set. (Note: normally there won't be
|
// than the ultimate set. (Note: normally there won't be
|
||||||
// unresolved inference variables here anyway, but there might be
|
// unresolved inference variables here anyway, but there might be
|
||||||
// during typeck under some circumstances.)
|
// during typeck under some circumstances.)
|
||||||
|
//
|
||||||
|
// FIXME(@lcnr): It's not really "always fine", having fewer implied
|
||||||
|
// bounds can be backward incompatible, e.g. #101951 was caused by
|
||||||
|
// us not dealing with inference vars in `TypeOutlives` predicates.
|
||||||
let obligations = wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP)
|
let obligations = wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// N.B., all of these predicates *ought* to be easily proven
|
// While these predicates should all be implied by other parts of
|
||||||
// true. In fact, their correctness is (mostly) implied by
|
// the program, they are still relevant as they may constrain
|
||||||
// other parts of the program. However, in #42552, we had
|
// inference variables, which is necessary to add the correct
|
||||||
// an annoying scenario where:
|
// implied bounds in some cases, mostly when dealing with projections.
|
||||||
//
|
|
||||||
// - Some `T::Foo` gets normalized, resulting in a
|
|
||||||
// variable `_1` and a `T: Trait<Foo=_1>` constraint
|
|
||||||
// (not sure why it couldn't immediately get
|
|
||||||
// solved). This result of `_1` got cached.
|
|
||||||
// - These obligations were dropped on the floor here,
|
|
||||||
// rather than being registered.
|
|
||||||
// - Then later we would get a request to normalize
|
|
||||||
// `T::Foo` which would result in `_1` being used from
|
|
||||||
// the cache, but hence without the `T: Trait<Foo=_1>`
|
|
||||||
// constraint. As a result, `_1` never gets resolved,
|
|
||||||
// and we get an ICE (in dropck).
|
|
||||||
//
|
|
||||||
// Therefore, we register any predicates involving
|
|
||||||
// inference variables. We restrict ourselves to those
|
|
||||||
// involving inference variables both for efficiency and
|
|
||||||
// to avoids duplicate errors that otherwise show up.
|
|
||||||
fulfill_cx.register_predicate_obligations(
|
fulfill_cx.register_predicate_obligations(
|
||||||
infcx,
|
infcx,
|
||||||
obligations.iter().filter(|o| o.predicate.has_infer_types_or_consts()).cloned(),
|
obligations.iter().filter(|o| o.predicate.has_infer_types_or_consts()).cloned(),
|
||||||
|
@ -96,10 +84,10 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
|
|
||||||
// From the full set of obligations, just filter down to the
|
// From the full set of obligations, just filter down to the
|
||||||
// region relationships.
|
// region relationships.
|
||||||
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
|
outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| {
|
||||||
assert!(!obligation.has_escaping_bound_vars());
|
assert!(!obligation.has_escaping_bound_vars());
|
||||||
match obligation.predicate.kind().no_bound_vars() {
|
match obligation.predicate.kind().no_bound_vars() {
|
||||||
None => vec![],
|
None => None,
|
||||||
Some(pred) => match pred {
|
Some(pred) => match pred {
|
||||||
ty::PredicateKind::Trait(..)
|
ty::PredicateKind::Trait(..)
|
||||||
| ty::PredicateKind::Subtype(..)
|
| ty::PredicateKind::Subtype(..)
|
||||||
|
@ -109,21 +97,18 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
| ty::PredicateKind::ObjectSafe(..)
|
| ty::PredicateKind::ObjectSafe(..)
|
||||||
| ty::PredicateKind::ConstEvaluatable(..)
|
| ty::PredicateKind::ConstEvaluatable(..)
|
||||||
| ty::PredicateKind::ConstEquate(..)
|
| ty::PredicateKind::ConstEquate(..)
|
||||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => vec![],
|
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
|
||||||
ty::PredicateKind::WellFormed(arg) => {
|
ty::PredicateKind::WellFormed(arg) => {
|
||||||
wf_args.push(arg);
|
wf_args.push(arg);
|
||||||
vec![]
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
|
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
|
||||||
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
|
Some(ty::OutlivesPredicate(r_a.into(), r_b))
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => {
|
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => {
|
||||||
let ty_a = infcx.resolve_vars_if_possible(ty_a);
|
Some(ty::OutlivesPredicate(ty_a.into(), r_b))
|
||||||
let mut components = smallvec![];
|
|
||||||
push_outlives_components(tcx, ty_a, &mut components);
|
|
||||||
implied_bounds_from_components(r_b, components)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -133,9 +118,27 @@ fn compute_implied_outlives_bounds<'tcx>(
|
||||||
// Ensure that those obligations that we had to solve
|
// Ensure that those obligations that we had to solve
|
||||||
// get solved *here*.
|
// get solved *here*.
|
||||||
match fulfill_cx.select_all_or_error(infcx).as_slice() {
|
match fulfill_cx.select_all_or_error(infcx).as_slice() {
|
||||||
[] => Ok(implied_bounds),
|
[] => (),
|
||||||
_ => Err(NoSolution),
|
_ => return Err(NoSolution),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We lazily compute the outlives components as
|
||||||
|
// `select_all_or_error` constrains inference variables.
|
||||||
|
let implied_bounds = outlives_bounds
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
|
||||||
|
ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
|
||||||
|
ty::GenericArgKind::Type(ty_a) => {
|
||||||
|
let ty_a = infcx.resolve_vars_if_possible(ty_a);
|
||||||
|
let mut components = smallvec![];
|
||||||
|
push_outlives_components(tcx, ty_a, &mut components);
|
||||||
|
implied_bounds_from_components(r_b, components)
|
||||||
|
}
|
||||||
|
ty::GenericArgKind::Const(_) => unreachable!(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(implied_bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When we have an implied bound that `T: 'a`, we can further break
|
/// When we have an implied bound that `T: 'a`, we can further break
|
||||||
|
|
|
@ -140,6 +140,7 @@ pub(crate) fn compare_impl_method<'tcx>(
|
||||||
///
|
///
|
||||||
/// Finally we register each of these predicates as an obligation and check that
|
/// Finally we register each of these predicates as an obligation and check that
|
||||||
/// they hold.
|
/// they hold.
|
||||||
|
#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))]
|
||||||
fn compare_predicate_entailment<'tcx>(
|
fn compare_predicate_entailment<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
impl_m: &AssocItem,
|
impl_m: &AssocItem,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue