Skip to content

Commit 35f5ef6

Browse files
committed
execute cycle check before we consider caching
1 parent 5486a86 commit 35f5ef6

File tree

1 file changed

+83
-62
lines changed

1 file changed

+83
-62
lines changed

src/librustc/traits/select.rs

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
874874
return Ok(result);
875875
}
876876

877+
// Check if this is a match for something already on the
878+
// stack. If so, we don't want to insert the result into the
879+
// main cache (it is cycle dependent) nor the provisional
880+
// cache (which is meant for things that have completed but
881+
// for a "backedge" -- this result *is* the backedge).
882+
if let Some(cycle_result) = self.check_evaluation_cycle(&stack) {
883+
return Ok(cycle_result);
884+
}
885+
877886
let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack));
878887
let result = result?;
879888

@@ -894,6 +903,74 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
894903
Ok(result)
895904
}
896905

906+
/// If there is any previous entry on the stack that precisely
907+
/// matches this obligation, then we can assume that the
908+
/// obligation is satisfied for now (still all other conditions
909+
/// must be met of course). One obvious case this comes up is
910+
/// marker traits like `Send`. Think of a linked list:
911+
///
912+
/// struct List<T> { data: T, next: Option<Box<List<T>>> }
913+
///
914+
/// `Box<List<T>>` will be `Send` if `T` is `Send` and
915+
/// `Option<Box<List<T>>>` is `Send`, and in turn
916+
/// `Option<Box<List<T>>>` is `Send` if `Box<List<T>>` is
917+
/// `Send`.
918+
///
919+
/// Note that we do this comparison using the `fresh_trait_ref`
920+
/// fields. Because these have all been freshened using
921+
/// `self.freshener`, we can be sure that (a) this will not
922+
/// affect the inferencer state and (b) that if we see two
923+
/// fresh regions with the same index, they refer to the same
924+
/// unbound type variable.
925+
fn check_evaluation_cycle(
926+
&mut self,
927+
stack: &TraitObligationStack<'_, 'tcx>,
928+
) -> Option<EvaluationResult> {
929+
if let Some(cycle_depth) = stack.iter()
930+
.skip(1) // skip top-most frame
931+
.find(|prev| stack.obligation.param_env == prev.obligation.param_env &&
932+
stack.fresh_trait_ref == prev.fresh_trait_ref)
933+
.map(|stack| stack.depth)
934+
{
935+
debug!(
936+
"evaluate_stack({:?}) --> recursive at depth {}",
937+
stack.fresh_trait_ref,
938+
cycle_depth,
939+
);
940+
941+
// If we have a stack like `A B C D E A`, where the top of
942+
// the stack is the final `A`, then this will iterate over
943+
// `A, E, D, C, B` -- i.e., all the participants apart
944+
// from the cycle head. We mark them as participating in a
945+
// cycle. This suppresses caching for those nodes. See
946+
// `in_cycle` field for more details.
947+
stack.update_reached_depth(cycle_depth);
948+
949+
// Subtle: when checking for a coinductive cycle, we do
950+
// not compare using the "freshened trait refs" (which
951+
// have erased regions) but rather the fully explicit
952+
// trait refs. This is important because it's only a cycle
953+
// if the regions match exactly.
954+
let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth);
955+
let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
956+
if self.coinductive_match(cycle) {
957+
debug!(
958+
"evaluate_stack({:?}) --> recursive, coinductive",
959+
stack.fresh_trait_ref
960+
);
961+
Some(EvaluatedToOk)
962+
} else {
963+
debug!(
964+
"evaluate_stack({:?}) --> recursive, inductive",
965+
stack.fresh_trait_ref
966+
);
967+
Some(EvaluatedToRecur)
968+
}
969+
} else {
970+
None
971+
}
972+
}
973+
897974
fn evaluate_stack<'o>(
898975
&mut self,
899976
stack: &TraitObligationStack<'o, 'tcx>,
@@ -970,66 +1047,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
9701047
return Ok(EvaluatedToUnknown);
9711048
}
9721049

973-
// If there is any previous entry on the stack that precisely
974-
// matches this obligation, then we can assume that the
975-
// obligation is satisfied for now (still all other conditions
976-
// must be met of course). One obvious case this comes up is
977-
// marker traits like `Send`. Think of a linked list:
978-
//
979-
// struct List<T> { data: T, next: Option<Box<List<T>>> }
980-
//
981-
// `Box<List<T>>` will be `Send` if `T` is `Send` and
982-
// `Option<Box<List<T>>>` is `Send`, and in turn
983-
// `Option<Box<List<T>>>` is `Send` if `Box<List<T>>` is
984-
// `Send`.
985-
//
986-
// Note that we do this comparison using the `fresh_trait_ref`
987-
// fields. Because these have all been freshened using
988-
// `self.freshener`, we can be sure that (a) this will not
989-
// affect the inferencer state and (b) that if we see two
990-
// fresh regions with the same index, they refer to the same
991-
// unbound type variable.
992-
if let Some(cycle_depth) = stack.iter()
993-
.skip(1) // skip top-most frame
994-
.find(|prev| stack.obligation.param_env == prev.obligation.param_env &&
995-
stack.fresh_trait_ref == prev.fresh_trait_ref)
996-
.map(|stack| stack.depth)
997-
{
998-
debug!("evaluate_stack({:?}) --> recursive", stack.fresh_trait_ref);
999-
1000-
// If we have a stack like `A B C D E A`, where the top of
1001-
// the stack is the final `A`, then this will iterate over
1002-
// `A, E, D, C, B` -- i.e., all the participants apart
1003-
// from the cycle head. We mark them as participating in a
1004-
// cycle. This suppresses caching for those nodes. See
1005-
// `in_cycle` field for more details.
1006-
for item in stack.iter().take_while(|s| s.depth > cycle_depth) {
1007-
debug!("evaluate_stack: marking {:?} as cycle participant", item.fresh_trait_ref);
1008-
item.update_reached_depth(cycle_depth);
1009-
}
1010-
1011-
// Subtle: when checking for a coinductive cycle, we do
1012-
// not compare using the "freshened trait refs" (which
1013-
// have erased regions) but rather the fully explicit
1014-
// trait refs. This is important because it's only a cycle
1015-
// if the regions match exactly.
1016-
let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth);
1017-
let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
1018-
if self.coinductive_match(cycle) {
1019-
debug!(
1020-
"evaluate_stack({:?}) --> recursive, coinductive",
1021-
stack.fresh_trait_ref
1022-
);
1023-
return Ok(EvaluatedToOk);
1024-
} else {
1025-
debug!(
1026-
"evaluate_stack({:?}) --> recursive, inductive",
1027-
stack.fresh_trait_ref
1028-
);
1029-
return Ok(EvaluatedToRecur);
1030-
}
1031-
}
1032-
10331050
match self.candidate_from_obligation(stack) {
10341051
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
10351052
Ok(None) => Ok(EvaluatedToAmbig),
@@ -3966,8 +3983,12 @@ impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
39663983
reached_depth,
39673984
);
39683985
debug!("update_reached_depth(reached_depth={})", reached_depth);
3969-
let v = self.reached_depth.get();
3970-
self.reached_depth.set(v.min(reached_depth));
3986+
let mut p = self;
3987+
while reached_depth < p.depth {
3988+
debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref);
3989+
p.reached_depth.set(p.reached_depth.get().min(reached_depth));
3990+
p = p.previous.head.unwrap();
3991+
}
39713992
}
39723993
}
39733994

0 commit comments

Comments
 (0)