mirror of https://github.com/rust-lang/rust.git
prevent illegal coinductive matching in trait evaluation
Previously, coinductive matching was only blocked on the fulfillment path, and ignored on the evaluation path.
This commit is contained in:
parent
703341051d
commit
14875fd3b7
|
@ -318,7 +318,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
|
|||
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>)
|
||||
where I: Clone + Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
|
||||
{
|
||||
if coinductive_match(self.selcx, cycle.clone()) {
|
||||
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
|
||||
debug!("process_child_obligations: coinductive match");
|
||||
} else {
|
||||
let cycle : Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
|
||||
|
@ -549,40 +549,6 @@ fn process_predicate<'a, 'gcx, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// For defaulted traits, we use a co-inductive strategy to solve, so
|
||||
/// that recursion is ok. This routine returns true if the top of the
|
||||
/// stack (`cycle[0]`):
|
||||
/// - is a defaulted trait, and
|
||||
/// - it also appears in the backtrace at some position `X`; and,
|
||||
/// - all the predicates at positions `X..` between `X` an the top are
|
||||
/// also defaulted traits.
|
||||
fn coinductive_match<'a,'c,'gcx,'tcx,I>(selcx: &mut SelectionContext<'a,'gcx,'tcx>,
|
||||
cycle: I) -> bool
|
||||
where I: Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
|
||||
'tcx: 'c
|
||||
{
|
||||
let mut cycle = cycle;
|
||||
cycle
|
||||
.all(|bt_obligation| {
|
||||
let result = coinductive_obligation(selcx, &bt_obligation.obligation);
|
||||
debug!("coinductive_match: bt_obligation={:?} coinductive={}",
|
||||
bt_obligation, result);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
fn coinductive_obligation<'a,'gcx,'tcx>(selcx: &SelectionContext<'a,'gcx,'tcx>,
|
||||
obligation: &PredicateObligation<'tcx>)
|
||||
-> bool {
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(ref data) => {
|
||||
selcx.tcx().trait_has_default_impl(data.def_id())
|
||||
}
|
||||
_ => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
|
||||
r_b: ty::Region<'tcx>,
|
||||
|
|
|
@ -703,14 +703,24 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
|||
// affect the inferencer state and (b) that if we see two
|
||||
// skolemized types with the same index, they refer to the
|
||||
// same unbound type variable.
|
||||
if
|
||||
if let Some(rec_index) =
|
||||
stack.iter()
|
||||
.skip(1) // skip top-most frame
|
||||
.any(|prev| stack.fresh_trait_ref == prev.fresh_trait_ref)
|
||||
.position(|prev| stack.fresh_trait_ref == prev.fresh_trait_ref)
|
||||
{
|
||||
debug!("evaluate_stack({:?}) --> recursive",
|
||||
stack.fresh_trait_ref);
|
||||
return EvaluatedToOk;
|
||||
let cycle = stack.iter().skip(1).take(rec_index+1);
|
||||
let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
|
||||
if self.coinductive_match(cycle) {
|
||||
debug!("evaluate_stack({:?}) --> recursive, coinductive",
|
||||
stack.fresh_trait_ref);
|
||||
return EvaluatedToOk;
|
||||
} else {
|
||||
debug!("evaluate_stack({:?}) --> recursive, inductive",
|
||||
stack.fresh_trait_ref);
|
||||
return EvaluatedToErr;
|
||||
}
|
||||
}
|
||||
|
||||
match self.candidate_from_obligation(stack) {
|
||||
|
@ -720,6 +730,33 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// For defaulted traits, we use a co-inductive strategy to solve, so
|
||||
/// that recursion is ok. This routine returns true if the top of the
|
||||
/// stack (`cycle[0]`):
|
||||
/// - is a defaulted trait, and
|
||||
/// - it also appears in the backtrace at some position `X`; and,
|
||||
/// - all the predicates at positions `X..` between `X` an the top are
|
||||
/// also defaulted traits.
|
||||
pub fn coinductive_match<I>(&mut self, cycle: I) -> bool
|
||||
where I: Iterator<Item=ty::Predicate<'tcx>>
|
||||
{
|
||||
let mut cycle = cycle;
|
||||
cycle.all(|predicate| self.coinductive_predicate(predicate))
|
||||
}
|
||||
|
||||
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
|
||||
let result = match predicate {
|
||||
ty::Predicate::Trait(ref data) => {
|
||||
self.tcx().trait_has_default_impl(data.def_id())
|
||||
}
|
||||
_ => {
|
||||
false
|
||||
}
|
||||
};
|
||||
debug!("coinductive_predicate({:?}) = {:?}", predicate, result);
|
||||
result
|
||||
}
|
||||
|
||||
/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
|
||||
/// obligations are met. Returns true if `candidate` remains viable after this further
|
||||
/// scrutiny.
|
||||
|
|
Loading…
Reference in New Issue