Skip to content

Commit 14875fd

Browse files
arielb1nikomatsakis
authored andcommitted
prevent illegal coinductive matching in trait evaluation
Previously, coinductive matching was only blocked on the fulfillment path, and ignored on the evaluation path.
1 parent 7033410 commit 14875fd

File tree

2 files changed

+41
-38
lines changed

2 files changed

+41
-38
lines changed

src/librustc/traits/fulfill.rs

+1-35
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
318318
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>)
319319
where I: Clone + Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
320320
{
321-
if coinductive_match(self.selcx, cycle.clone()) {
321+
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
322322
debug!("process_child_obligations: coinductive match");
323323
} else {
324324
let cycle : Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
@@ -549,40 +549,6 @@ fn process_predicate<'a, 'gcx, 'tcx>(
549549
}
550550
}
551551

552-
/// For defaulted traits, we use a co-inductive strategy to solve, so
553-
/// that recursion is ok. This routine returns true if the top of the
554-
/// stack (`cycle[0]`):
555-
/// - is a defaulted trait, and
556-
/// - it also appears in the backtrace at some position `X`; and,
557-
/// - all the predicates at positions `X..` between `X` an the top are
558-
/// also defaulted traits.
559-
fn coinductive_match<'a,'c,'gcx,'tcx,I>(selcx: &mut SelectionContext<'a,'gcx,'tcx>,
560-
cycle: I) -> bool
561-
where I: Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
562-
'tcx: 'c
563-
{
564-
let mut cycle = cycle;
565-
cycle
566-
.all(|bt_obligation| {
567-
let result = coinductive_obligation(selcx, &bt_obligation.obligation);
568-
debug!("coinductive_match: bt_obligation={:?} coinductive={}",
569-
bt_obligation, result);
570-
result
571-
})
572-
}
573-
574-
fn coinductive_obligation<'a,'gcx,'tcx>(selcx: &SelectionContext<'a,'gcx,'tcx>,
575-
obligation: &PredicateObligation<'tcx>)
576-
-> bool {
577-
match obligation.predicate {
578-
ty::Predicate::Trait(ref data) => {
579-
selcx.tcx().trait_has_default_impl(data.def_id())
580-
}
581-
_ => {
582-
false
583-
}
584-
}
585-
}
586552

587553
fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
588554
r_b: ty::Region<'tcx>,

src/librustc/traits/select.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -703,14 +703,24 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
703703
// affect the inferencer state and (b) that if we see two
704704
// skolemized types with the same index, they refer to the
705705
// same unbound type variable.
706-
if
706+
if let Some(rec_index) =
707707
stack.iter()
708708
.skip(1) // skip top-most frame
709-
.any(|prev| stack.fresh_trait_ref == prev.fresh_trait_ref)
709+
.position(|prev| stack.fresh_trait_ref == prev.fresh_trait_ref)
710710
{
711711
debug!("evaluate_stack({:?}) --> recursive",
712712
stack.fresh_trait_ref);
713-
return EvaluatedToOk;
713+
let cycle = stack.iter().skip(1).take(rec_index+1);
714+
let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
715+
if self.coinductive_match(cycle) {
716+
debug!("evaluate_stack({:?}) --> recursive, coinductive",
717+
stack.fresh_trait_ref);
718+
return EvaluatedToOk;
719+
} else {
720+
debug!("evaluate_stack({:?}) --> recursive, inductive",
721+
stack.fresh_trait_ref);
722+
return EvaluatedToErr;
723+
}
714724
}
715725

716726
match self.candidate_from_obligation(stack) {
@@ -720,6 +730,33 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
720730
}
721731
}
722732

733+
/// For defaulted traits, we use a co-inductive strategy to solve, so
734+
/// that recursion is ok. This routine returns true if the top of the
735+
/// stack (`cycle[0]`):
736+
/// - is a defaulted trait, and
737+
/// - it also appears in the backtrace at some position `X`; and,
738+
/// - all the predicates at positions `X..` between `X` an the top are
739+
/// also defaulted traits.
740+
pub fn coinductive_match<I>(&mut self, cycle: I) -> bool
741+
where I: Iterator<Item=ty::Predicate<'tcx>>
742+
{
743+
let mut cycle = cycle;
744+
cycle.all(|predicate| self.coinductive_predicate(predicate))
745+
}
746+
747+
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
748+
let result = match predicate {
749+
ty::Predicate::Trait(ref data) => {
750+
self.tcx().trait_has_default_impl(data.def_id())
751+
}
752+
_ => {
753+
false
754+
}
755+
};
756+
debug!("coinductive_predicate({:?}) = {:?}", predicate, result);
757+
result
758+
}
759+
723760
/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
724761
/// obligations are met. Returns true if `candidate` remains viable after this further
725762
/// scrutiny.

0 commit comments

Comments
 (0)