Fix bugs in evaluating WellFormed predicates

- List the nestsed obligations in an order that works with the
  single pass used by evaluation
- Propagate recursion depth correctly
This commit is contained in:
Matthew Jasper 2020-07-22 22:43:18 +01:00
parent f52b2d8890
commit bc08b791bc
8 changed files with 117 additions and 42 deletions

View file

@ -449,6 +449,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
self.selcx.infcx(),
obligation.param_env,
obligation.cause.body_id,
obligation.recursion_depth + 1,
arg,
obligation.cause.span,
) {

View file

@ -139,9 +139,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
});
// Require that the projection is well-formed.
let self_ty = obligation.predicate.skip_binder().self_ty();
obligations.push(Obligation::new(
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&obligation.self_ty());
let self_ty = normalize_with_depth_to(
self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
&self_ty,
&mut obligations,
);
obligations.push(Obligation::with_depth(
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()),
));
@ -333,9 +342,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// relying on projections in the impl-trait-ref.
//
// e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
impl_obligations.append(&mut substs.obligations);
substs.obligations.append(&mut impl_obligations);
ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations }
ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: substs.obligations }
}
fn confirm_object_candidate(

View file

@ -343,7 +343,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Err(SelectionError::Overflow)
}
Err(e) => Err(e),
Ok(candidate) => Ok(Some(candidate)),
Ok(candidate) => {
debug!("select: candidate = {:?}", candidate);
Ok(Some(candidate))
}
}
}
@ -413,9 +416,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
predicates: I,
) -> Result<EvaluationResult, OverflowError>
where
I: IntoIterator<Item = PredicateObligation<'tcx>>,
I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
{
let mut result = EvaluatedToOk;
debug!("evaluate_predicates_recursively({:?})", predicates);
for obligation in predicates {
let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval);
@ -436,7 +440,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
debug!(
"evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})",
"evaluate_predicate_recursively(obligation={:?}, previous_stack={:?})",
previous_stack.head(),
obligation
);
@ -479,15 +483,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.infcx,
obligation.param_env,
obligation.cause.body_id,
obligation.recursion_depth + 1,
arg,
obligation.cause.span,
) {
Some(mut obligations) => {
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
self.evaluate_predicates_recursively(
previous_stack,
obligations.into_iter(),
)
self.evaluate_predicates_recursively(previous_stack, obligations)
}
None => Ok(EvaluatedToAmbig),
},
@ -511,10 +513,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
match project::poly_project_and_unify_type(self, &project_obligation) {
Ok(Ok(Some(mut subobligations))) => {
self.add_depth(subobligations.iter_mut(), obligation.recursion_depth);
let result = self.evaluate_predicates_recursively(
previous_stack,
subobligations.into_iter(),
);
let result = self
.evaluate_predicates_recursively(previous_stack, subobligations);
if let Some(key) =
ProjectionCacheKey::from_poly_projection_predicate(self, data)
{
@ -879,10 +879,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let result = self.evaluation_probe(|this| {
let candidate = (*candidate).clone();
match this.confirm_candidate(stack.obligation, candidate) {
Ok(selection) => this.evaluate_predicates_recursively(
stack.list(),
selection.nested_obligations().into_iter(),
),
Ok(selection) => {
debug!("evaluate_candidate: selection = {:?}", selection);
this.evaluate_predicates_recursively(
stack.list(),
selection.nested_obligations().into_iter(),
)
}
Err(..) => Ok(EvaluatedToErr),
}
})?;
@ -1231,9 +1234,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> Result<EvaluationResult, OverflowError> {
self.evaluation_probe(|this| {
match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
Ok(obligations) => {
this.evaluate_predicates_recursively(stack.list(), obligations.into_iter())
}
Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations),
Err(()) => Ok(EvaluatedToErr),
}
})

View file

@ -20,6 +20,7 @@ pub fn obligations<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_id: hir::HirId,
recursion_depth: usize,
arg: GenericArg<'tcx>,
span: Span,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
@ -59,7 +60,8 @@ pub fn obligations<'a, 'tcx>(
GenericArgKind::Lifetime(..) => return Some(Vec::new()),
};
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
let mut wf =
WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None };
wf.compute(arg);
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
@ -80,7 +82,8 @@ pub fn trait_obligations<'a, 'tcx>(
span: Span,
item: Option<&'tcx hir::Item<'tcx>>,
) -> Vec<traits::PredicateObligation<'tcx>> {
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item };
let mut wf =
WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth: 0, item };
wf.compute_trait_ref(trait_ref, Elaborate::All);
wf.normalize()
}
@ -92,7 +95,15 @@ pub fn predicate_obligations<'a, 'tcx>(
predicate: ty::Predicate<'tcx>,
span: Span,
) -> Vec<traits::PredicateObligation<'tcx>> {
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
let mut wf = WfPredicates {
infcx,
param_env,
body_id,
span,
out: vec![],
recursion_depth: 0,
item: None,
};
// It's ok to skip the binder here because wf code is prepared for it
match predicate.skip_binders() {
@ -142,6 +153,7 @@ struct WfPredicates<'a, 'tcx> {
body_id: hir::HirId,
span: Span,
out: Vec<traits::PredicateObligation<'tcx>>,
recursion_depth: usize,
item: Option<&'tcx hir::Item<'tcx>>,
}
@ -249,19 +261,19 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
for mut obligation in self.out {
assert!(!obligation.has_escaping_bound_vars());
let mut selcx = traits::SelectionContext::new(infcx);
let i = obligations.len();
// Don't normalize the whole obligation, the param env is either
// already normalized, or we're currently normalizing the
// param_env. Either way we should only normalize the predicate.
let normalized_predicate = traits::normalize_to(
let normalized_predicate = traits::project::normalize_with_depth_to(
&mut selcx,
param_env,
cause.clone(),
self.recursion_depth,
&obligation.predicate,
&mut obligations,
);
obligation.predicate = normalized_predicate;
obligations.insert(i, obligation);
obligations.push(obligation);
}
obligations
}
@ -274,6 +286,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
debug!("compute_trait_ref obligations {:?}", obligations);
let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;
let depth = self.recursion_depth;
let item = self.item;
@ -295,7 +308,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
&obligation.predicate,
tcx.associated_items(trait_ref.def_id).in_definition_order(),
);
traits::Obligation::new(cause, param_env, obligation.predicate)
traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate)
};
if let Elaborate::All = elaborate {
@ -324,8 +337,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
new_cause.make_mut().span = self_ty.span;
}
}
traits::Obligation::new(
traits::Obligation::with_depth(
new_cause,
depth,
param_env,
ty::PredicateAtom::WellFormed(arg).to_predicate(tcx),
)
@ -363,6 +377,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let tcx = self.tcx();
let cause = self.cause(traits::MiscObligation);
let param_env = self.param_env;
let depth = self.recursion_depth;
self.out.extend(
data.substs
@ -372,8 +387,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
})
.filter(|arg| !arg.has_escaping_bound_vars())
.map(|arg| {
traits::Obligation::new(
traits::Obligation::with_depth(
cause.clone(),
depth,
param_env,
ty::PredicateKind::WellFormed(arg).to_predicate(tcx),
)
@ -388,8 +404,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None),
substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
};
self.out.push(traits::Obligation::new(
self.out.push(traits::Obligation::with_depth(
cause,
self.recursion_depth,
self.param_env,
trait_ref.without_const().to_predicate(self.infcx.tcx),
));
@ -400,6 +417,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
fn compute(&mut self, arg: GenericArg<'tcx>) {
let mut walker = arg.walk();
let param_env = self.param_env;
let depth = self.recursion_depth;
while let Some(arg) = walker.next() {
let ty = match arg.unpack() {
GenericArgKind::Type(ty) => ty,
@ -419,8 +437,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let predicate = ty::PredicateAtom::ConstEvaluatable(def, substs)
.to_predicate(self.tcx());
let cause = self.cause(traits::MiscObligation);
self.out.push(traits::Obligation::new(
self.out.push(traits::Obligation::with_depth(
cause,
self.recursion_depth,
self.param_env,
predicate,
));
@ -435,8 +454,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
val: ty::ConstKind::Infer(resolved),
..*constant
});
self.out.push(traits::Obligation::new(
self.out.push(traits::Obligation::with_depth(
cause,
self.recursion_depth,
self.param_env,
ty::PredicateAtom::WellFormed(resolved_constant.into())
.to_predicate(self.tcx()),
@ -521,8 +541,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// WfReference
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
self.out.push(traits::Obligation::new(
self.out.push(traits::Obligation::with_depth(
cause,
depth,
param_env,
ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(rty, r))
.to_predicate(self.tcx()),
@ -612,8 +633,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let component_traits = data.auto_traits().chain(data.principal_def_id());
let tcx = self.tcx();
self.out.extend(component_traits.map(|did| {
traits::Obligation::new(
traits::Obligation::with_depth(
cause.clone(),
depth,
param_env,
ty::PredicateAtom::ObjectSafe(did).to_predicate(tcx),
)
@ -638,8 +660,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
// Not yet resolved, but we've made progress.
let cause = self.cause(traits::MiscObligation);
self.out.push(traits::Obligation::new(
self.out.push(traits::Obligation::with_depth(
cause,
self.recursion_depth,
param_env,
ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx()),
));
@ -676,7 +699,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
.zip(origins.into_iter().rev())
.map(|((pred, span), origin_def_id)| {
let cause = self.cause(traits::BindingObligation(origin_def_id, span));
traits::Obligation::new(cause, self.param_env, pred)
traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred)
})
.filter(|pred| !pred.has_escaping_bound_vars())
.collect()
@ -729,8 +752,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound));
let outlives =
ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound));
self.out.push(traits::Obligation::new(
self.out.push(traits::Obligation::with_depth(
cause,
self.recursion_depth,
self.param_env,
outlives.to_predicate(self.infcx.tcx),
));