Skip to content

Commit a9cd294

Browse files
committed
Auto merge of #77720 - matthewjasper:fix-trait-ices, r=nikomatsakis
Fix trait solving ICEs - Selection candidates that are known to be applicable are preferred over candidates that are not. - Don't ICE if a projection/object candidate is no longer applicable (this can happen due to cycles in normalization) - Normalize supertraits when finding trait object candidates Closes #77653 Closes #77656 r? `@nikomatsakis`
2 parents 500ddc5 + 50dde2e commit a9cd294

File tree

6 files changed

+141
-86
lines changed

6 files changed

+141
-86
lines changed

Diff for: compiler/rustc_middle/src/traits/select.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ pub enum SelectionCandidate<'tcx> {
127127

128128
TraitAliasCandidate(DefId),
129129

130-
ObjectCandidate,
130+
/// Matching `dyn Trait` with a supertrait of `Trait`. The index is the
131+
/// position in the iterator returned by
132+
/// `rustc_infer::traits::util::supertraits`.
133+
ObjectCandidate(usize),
131134

132135
BuiltinObjectCandidate,
133136

Diff for: compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -642,24 +642,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
642642

643643
debug!(?poly_trait_ref, "assemble_candidates_from_object_ty");
644644

645+
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
646+
let placeholder_trait_predicate =
647+
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
648+
645649
// Count only those upcast versions that match the trait-ref
646650
// we are looking for. Specifically, do not only check for the
647651
// correct trait, but also the correct type parameters.
648652
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
649653
// but `Foo` is declared as `trait Foo: Bar<u32>`.
650-
let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
651-
.filter(|upcast_trait_ref| {
652-
self.infcx
653-
.probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok())
654+
let candidate_supertraits = util::supertraits(self.tcx(), poly_trait_ref)
655+
.enumerate()
656+
.filter(|&(_, upcast_trait_ref)| {
657+
self.infcx.probe(|_| {
658+
self.match_normalize_trait_ref(
659+
obligation,
660+
upcast_trait_ref,
661+
placeholder_trait_predicate.trait_ref,
662+
)
663+
.is_ok()
664+
})
654665
})
655-
.count();
666+
.map(|(idx, _)| ObjectCandidate(idx));
656667

657-
if upcast_trait_refs > 1 {
658-
// Can be upcast in many ways; need more type information.
659-
candidates.ambiguous = true;
660-
} else if upcast_trait_refs == 1 {
661-
candidates.vec.push(ObjectCandidate);
662-
}
668+
candidates.vec.extend(candidate_supertraits);
663669
})
664670
}
665671

Diff for: compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+44-53
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,15 @@ 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

76+
ObjectCandidate(idx) => {
77+
let data = self.confirm_object_candidate(obligation, idx)?;
78+
Ok(ImplSource::Object(data))
79+
}
80+
7681
ClosureCandidate => {
7782
let vtable_closure = self.confirm_closure_candidate(obligation)?;
7883
Ok(ImplSource::Closure(vtable_closure))
@@ -97,11 +102,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
97102
Ok(ImplSource::TraitAlias(data))
98103
}
99104

100-
ObjectCandidate => {
101-
let data = self.confirm_object_candidate(obligation);
102-
Ok(ImplSource::Object(data))
103-
}
104-
105105
BuiltinObjectCandidate => {
106106
// This indicates something like `Trait + Send: Send`. In this case, we know that
107107
// this holds because that's what the object type is telling us, and there's really
@@ -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

@@ -371,9 +365,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
371365
fn confirm_object_candidate(
372366
&mut self,
373367
obligation: &TraitObligation<'tcx>,
374-
) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> {
375-
debug!(?obligation, "confirm_object_candidate");
368+
index: usize,
369+
) -> Result<ImplSourceObjectData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
376370
let tcx = self.tcx();
371+
debug!(?obligation, ?index, "confirm_object_candidate");
377372

378373
let trait_predicate =
379374
self.infcx.replace_bound_vars_with_placeholders(&obligation.predicate);
@@ -399,43 +394,39 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
399394
})
400395
.with_self_ty(self.tcx(), self_ty);
401396

402-
let mut upcast_trait_ref = None;
403397
let mut nested = vec![];
404-
let vtable_base;
405398

406-
{
407-
// We want to find the first supertrait in the list of
408-
// supertraits that we can unify with, and do that
409-
// unification. We know that there is exactly one in the list
410-
// where we can unify, because otherwise select would have
411-
// reported an ambiguity. (When we do find a match, also
412-
// record it for later.)
413-
let nonmatching = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref))
414-
.take_while(|&t| {
415-
match self.infcx.commit_if_ok(|_| {
416-
self.infcx
417-
.at(&obligation.cause, obligation.param_env)
418-
.sup(obligation_trait_ref, t)
419-
.map(|InferOk { obligations, .. }| obligations)
420-
.map_err(|_| ())
421-
}) {
422-
Ok(obligations) => {
423-
upcast_trait_ref = Some(t);
424-
nested.extend(obligations);
425-
false
426-
}
427-
Err(_) => true,
428-
}
429-
});
399+
let mut supertraits = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref));
430400

431-
// Additionally, for each of the non-matching predicates that
432-
// we pass over, we sum up the set of number of vtable
433-
// entries, so that we can compute the offset for the selected
434-
// trait.
435-
vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum();
436-
}
401+
// For each of the non-matching predicates that
402+
// we pass over, we sum up the set of number of vtable
403+
// entries, so that we can compute the offset for the selected
404+
// trait.
405+
let vtable_base = supertraits
406+
.by_ref()
407+
.take(index)
408+
.map(|t| super::util::count_own_vtable_entries(tcx, t))
409+
.sum();
410+
411+
let unnormalized_upcast_trait_ref =
412+
supertraits.next().expect("supertraits iterator no longer has as many elements");
413+
414+
let upcast_trait_ref = normalize_with_depth_to(
415+
self,
416+
obligation.param_env,
417+
obligation.cause.clone(),
418+
obligation.recursion_depth + 1,
419+
&unnormalized_upcast_trait_ref,
420+
&mut nested,
421+
);
437422

438-
let upcast_trait_ref = upcast_trait_ref.unwrap();
423+
nested.extend(self.infcx.commit_if_ok(|_| {
424+
self.infcx
425+
.at(&obligation.cause, obligation.param_env)
426+
.sup(obligation_trait_ref, upcast_trait_ref)
427+
.map(|InferOk { obligations, .. }| obligations)
428+
.map_err(|_| Unimplemented)
429+
})?);
439430

440431
// Check supertraits hold. This is so that their associated type bounds
441432
// will be checked in the code below.
@@ -501,7 +492,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
501492
}
502493

503494
debug!(?nested, "object nested obligations");
504-
ImplSourceObjectData { upcast_trait_ref, vtable_base, nested }
495+
Ok(ImplSourceObjectData { upcast_trait_ref, vtable_base, nested })
505496
}
506497

507498
fn confirm_fn_pointer_candidate(

Diff for: compiler/rustc_trait_selection/src/traits/select/mod.rs

+14-21
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
}
@@ -1179,7 +1174,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11791174
if let ty::PredicateAtom::Trait(pred, _) = bound_predicate.skip_binder() {
11801175
let bound = bound_predicate.rebind(pred.trait_ref);
11811176
if self.infcx.probe(|_| {
1182-
match self.match_projection(
1177+
match self.match_normalize_trait_ref(
11831178
obligation,
11841179
bound,
11851180
placeholder_trait_predicate.trait_ref,
@@ -1207,7 +1202,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12071202
/// Equates the trait in `obligation` with trait bound. If the two traits
12081203
/// can be equated and the normalized trait bound doesn't contain inference
12091204
/// variables or placeholders, the normalized bound is returned.
1210-
fn match_projection(
1205+
fn match_normalize_trait_ref(
12111206
&mut self,
12121207
obligation: &TraitObligation<'tcx>,
12131208
trait_bound: ty::PolyTraitRef<'tcx>,
@@ -1357,10 +1352,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13571352
| BuiltinUnsizeCandidate
13581353
| BuiltinCandidate { .. }
13591354
| TraitAliasCandidate(..)
1360-
| ObjectCandidate
1355+
| ObjectCandidate(_)
13611356
| ProjectionCandidate(_),
13621357
) => !is_global(cand),
1363-
(ObjectCandidate | ProjectionCandidate(_), ParamCandidate(ref cand)) => {
1358+
(ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => {
13641359
// Prefer these to a global where-clause bound
13651360
// (see issue #50825).
13661361
is_global(cand)
@@ -1381,20 +1376,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13811376
is_global(cand) && other.evaluation.must_apply_modulo_regions()
13821377
}
13831378

1384-
(ProjectionCandidate(i), ProjectionCandidate(j)) => {
1385-
// Arbitrarily pick the first candidate for backwards
1379+
(ProjectionCandidate(i), ProjectionCandidate(j))
1380+
| (ObjectCandidate(i), ObjectCandidate(j)) => {
1381+
// Arbitrarily pick the lower numbered candidate for backwards
13861382
// compatibility reasons. Don't let this affect inference.
1387-
i > j && !needs_infer
1383+
i < j && !needs_infer
13881384
}
1389-
(ObjectCandidate, ObjectCandidate) => bug!("Duplicate object candidate"),
1390-
(ObjectCandidate, ProjectionCandidate(_))
1391-
| (ProjectionCandidate(_), ObjectCandidate) => {
1385+
(ObjectCandidate(_), ProjectionCandidate(_))
1386+
| (ProjectionCandidate(_), ObjectCandidate(_)) => {
13921387
bug!("Have both object and projection candidate")
13931388
}
13941389

13951390
// Arbitrarily give projection and object candidates priority.
13961391
(
1397-
ObjectCandidate | ProjectionCandidate(_),
1392+
ObjectCandidate(_) | ProjectionCandidate(_),
13981393
ImplCandidate(..)
13991394
| ClosureCandidate
14001395
| GeneratorCandidate
@@ -1414,7 +1409,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14141409
| BuiltinUnsizeCandidate
14151410
| BuiltinCandidate { .. }
14161411
| TraitAliasCandidate(..),
1417-
ObjectCandidate | ProjectionCandidate(_),
1412+
ObjectCandidate(_) | ProjectionCandidate(_),
14181413
) => false,
14191414

14201415
(&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
@@ -1890,9 +1885,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
18901885

18911886
/// Normalize `where_clause_trait_ref` and try to match it against
18921887
/// `obligation`. If successful, return any predicates that
1893-
/// result from the normalization. Normalization is necessary
1894-
/// because where-clauses are stored in the parameter environment
1895-
/// unnormalized.
1888+
/// result from the normalization.
18961889
fn match_where_clause_trait_ref(
18971890
&mut self,
18981891
obligation: &TraitObligation<'tcx>,
+25
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() {}

Diff for: src/test/ui/traits/normalize-super-trait.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Regression test for #77653
2+
// When monomorphizing `f` we need to prove `dyn Derived<()>: Base<()>`. This
3+
// requires us to normalize the `Base<<() as Proj>::S>` to `Base<()>` when
4+
// comparing the supertrait `Derived<()>` to the expected trait.
5+
6+
// build-pass
7+
8+
trait Proj {
9+
type S;
10+
}
11+
12+
impl Proj for () {
13+
type S = ();
14+
}
15+
16+
impl Proj for i32 {
17+
type S = i32;
18+
}
19+
20+
trait Base<T> {
21+
fn is_base(&self);
22+
}
23+
24+
trait Derived<B: Proj>: Base<B::S> + Base<()> {
25+
fn is_derived(&self);
26+
}
27+
28+
fn f<P: Proj>(obj: &dyn Derived<P>) {
29+
obj.is_derived();
30+
Base::<P::S>::is_base(obj);
31+
Base::<()>::is_base(obj);
32+
}
33+
34+
fn main() {
35+
let x: fn(_) = f::<()>;
36+
let x: fn(_) = f::<i32>;
37+
}

0 commit comments

Comments
 (0)