Skip to content

Commit 6c43a64

Browse files
committed
Fix ICE from projection cycle
Cycles in normalization can cause evaluations to change from Unknown to Err. This means that some selection that were applicable no longer are. To avoid this: * Selection candidates that are known to be applicable are prefered over candidates that are not. * We don't ICE if a candidate is no longer applicable.
1 parent 22e6b9c commit 6c43a64

File tree

3 files changed

+34
-20
lines changed

3 files changed

+34
-20
lines changed

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
6969
}
7070

7171
ProjectionCandidate(idx) => {
72-
let obligations = self.confirm_projection_candidate(obligation, idx);
72+
let obligations = self.confirm_projection_candidate(obligation, idx)?;
7373
Ok(ImplSource::Param(obligations))
7474
}
7575

@@ -120,7 +120,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
120120
&mut self,
121121
obligation: &TraitObligation<'tcx>,
122122
idx: usize,
123-
) -> Vec<PredicateObligation<'tcx>> {
123+
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
124124
self.infcx.commit_unconditionally(|_| {
125125
let tcx = self.tcx();
126126

@@ -148,19 +148,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
148148
&mut obligations,
149149
);
150150

151-
obligations.extend(
151+
obligations.extend(self.infcx.commit_if_ok(|_| {
152152
self.infcx
153153
.at(&obligation.cause, obligation.param_env)
154154
.sup(placeholder_trait_predicate.trait_ref.to_poly_trait_ref(), candidate)
155155
.map(|InferOk { obligations, .. }| obligations)
156-
.unwrap_or_else(|_| {
157-
bug!(
158-
"Projection bound `{:?}` was applicable to `{:?}` but now is not",
159-
candidate,
160-
obligation
161-
);
162-
}),
163-
);
156+
.map_err(|_| Unimplemented)
157+
})?);
164158

165159
if let ty::Projection(..) = placeholder_self_ty.kind() {
166160
for predicate in tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates {
@@ -181,7 +175,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
181175
}
182176
}
183177

184-
obligations
178+
Ok(obligations)
185179
})
186180
}
187181

compiler/rustc_trait_selection/src/traits/select/mod.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -518,12 +518,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
518518
result
519519
}
520520
Ok(Ok(None)) => Ok(EvaluatedToAmbig),
521-
// EvaluatedToRecur might also be acceptable here, but use
522-
// Unknown for now because it means that we won't dismiss a
523-
// selection candidate solely because it has a projection
524-
// cycle. This is closest to the previous behavior of
525-
// immediately erroring.
526-
Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown),
521+
Ok(Err(project::InProgress)) => Ok(EvaluatedToRecur),
527522
Err(_) => Ok(EvaluatedToErr),
528523
}
529524
}
@@ -1382,9 +1377,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13821377
}
13831378

13841379
(ProjectionCandidate(i), ProjectionCandidate(j)) => {
1385-
// Arbitrarily pick the first candidate for backwards
1380+
// Arbitrarily pick the lower numbered candidate for backwards
13861381
// compatibility reasons. Don't let this affect inference.
1387-
i > j && !needs_infer
1382+
i < j && !needs_infer
13881383
}
13891384
(ObjectCandidate, ObjectCandidate) => bug!("Duplicate object candidate"),
13901385
(ObjectCandidate, ProjectionCandidate(_))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Regression test for #77656
2+
3+
// check-pass
4+
5+
trait Value: PartialOrd {}
6+
7+
impl<T: PartialOrd> Value for T {}
8+
9+
trait Distance
10+
where
11+
Self: PartialOrd<<Self as Distance>::Value>,
12+
Self: PartialOrd,
13+
{
14+
type Value: Value;
15+
}
16+
17+
impl<T: Value> Distance for T {
18+
type Value = T;
19+
}
20+
21+
trait Proximity<T = Self> {
22+
type Distance: Distance;
23+
}
24+
25+
fn main() {}

0 commit comments

Comments
 (0)