Skip to content

Commit cfee495

Browse files
committed
Handle multiple applicable projection candidates
1 parent bc08b79 commit cfee495

File tree

6 files changed

+82
-37
lines changed

6 files changed

+82
-37
lines changed

compiler/rustc_middle/src/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ rustc_queries! {
157157
}
158158

159159
/// Returns the list of bounds that can be used for
160-
/// `SelectionCandidate::ProjectionCandidate` and
160+
/// `SelectionCandidate::ProjectionCandidate(_)` and
161161
/// `ProjectionTyCandidate::TraitDef`.
162162
/// Specifically this is the bounds written on the trait's type
163163
/// definition, or those after the `impl` keyword

compiler/rustc_middle/src/traits/select.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ pub enum SelectionCandidate<'tcx> {
105105
ImplCandidate(DefId),
106106
AutoImplCandidate(DefId),
107107

108-
/// This is a trait matching with a projected type as `Self`, and
109-
/// we found an applicable bound in the trait definition.
110-
ProjectionCandidate,
108+
/// This is a trait matching with a projected type as `Self`, and we found
109+
/// an applicable bound in the trait definition. The `usize` is an index
110+
/// into the list returned by `tcx.item_bounds`.
111+
ProjectionCandidate(usize),
111112

112113
/// Implementation of a `Fn`-family trait by one of the anonymous types
113114
/// generated for a `||` expression.

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
323323
_ => return,
324324
}
325325

326-
let result = self.infcx.probe(|_| {
327-
self.match_projection_obligation_against_definition_bounds(obligation).is_some()
328-
});
326+
let result = self
327+
.infcx
328+
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
329329

330-
if result {
331-
candidates.vec.push(ProjectionCandidate);
330+
for predicate_index in result {
331+
candidates.vec.push(ProjectionCandidate(predicate_index));
332332
}
333333
}
334334

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
7070
Ok(ImplSource::AutoImpl(data))
7171
}
7272

73-
ProjectionCandidate => {
74-
let obligations = self.confirm_projection_candidate(obligation);
73+
ProjectionCandidate(idx) => {
74+
let obligations = self.confirm_projection_candidate(obligation, idx);
7575
Ok(ImplSource::Param(obligations))
7676
}
7777

@@ -121,11 +121,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
121121
fn confirm_projection_candidate(
122122
&mut self,
123123
obligation: &TraitObligation<'tcx>,
124+
idx: usize,
124125
) -> Vec<PredicateObligation<'tcx>> {
125126
self.infcx.commit_unconditionally(|_| {
126-
let candidate = self
127-
.match_projection_obligation_against_definition_bounds(obligation)
128-
.unwrap_or_else(|| bug!("Can't find selected projection candidate"));
127+
let tcx = self.tcx();
128+
129+
let bound_self_ty = self.infcx.shallow_resolve(obligation.self_ty());
130+
let (def_id, substs) = match bound_self_ty.skip_binder().kind {
131+
ty::Projection(proj) => (proj.item_def_id, proj.substs),
132+
ty::Opaque(def_id, substs) => (def_id, substs),
133+
_ => bug!("projection candidate for unexpected type: {:?}", bound_self_ty),
134+
};
135+
136+
let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs);
137+
let candidate = candidate_predicate
138+
.to_opt_poly_trait_ref()
139+
.expect("projection candidate is not a trait predicate");
129140
let mut obligations = self
130141
.infcx
131142
.at(&obligation.cause, obligation.param_env)
@@ -139,7 +150,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
139150
);
140151
});
141152
// Require that the projection is well-formed.
142-
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&obligation.self_ty());
153+
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&bound_self_ty);
143154
let self_ty = normalize_with_depth_to(
144155
self,
145156
obligation.param_env,
@@ -152,7 +163,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
152163
obligation.cause.clone(),
153164
obligation.recursion_depth + 1,
154165
obligation.param_env,
155-
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()),
166+
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(tcx),
156167
));
157168
obligations
158169
})

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

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,11 +1163,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11631163
///
11641164
/// Given an obligation like `<T as Foo>::Bar: Baz` where the self type is
11651165
/// a projection, look at the bounds of `T::Bar`, see if we can find a
1166-
/// `Baz` bound and it there is one it returns it.
1166+
/// `Baz` bound. We return indexes into the list returned by
1167+
/// `tcx.item_bounds` for any applicable bounds.
11671168
fn match_projection_obligation_against_definition_bounds(
11681169
&mut self,
11691170
obligation: &TraitObligation<'tcx>,
1170-
) -> Option<ty::PolyTraitRef<'tcx>> {
1171+
) -> smallvec::SmallVec<[usize; 2]> {
11711172
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
11721173
let placeholder_trait_predicate =
11731174
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
@@ -1192,25 +1193,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11921193
};
11931194
let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
11941195

1195-
let matching_bound = bounds.iter().find_map(|bound| {
1196-
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
1197-
let bound = ty::Binder::bind(pred.trait_ref);
1198-
if self.infcx.probe(|_| {
1199-
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref)
1196+
let matching_bounds = bounds
1197+
.iter()
1198+
.enumerate()
1199+
.filter_map(|(idx, bound)| {
1200+
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
1201+
let bound = ty::Binder::bind(pred.trait_ref);
1202+
if self.infcx.probe(|_| {
1203+
self.match_projection(
1204+
obligation,
1205+
bound,
1206+
placeholder_trait_predicate.trait_ref,
1207+
)
12001208
.is_ok()
1201-
}) {
1202-
return Some(bound);
1209+
}) {
1210+
return Some(idx);
1211+
}
12031212
}
1204-
}
1205-
None
1206-
});
1213+
None
1214+
})
1215+
.collect();
12071216

12081217
debug!(
12091218
"match_projection_obligation_against_definition_bounds: \
1210-
matching_bound={:?}",
1211-
matching_bound
1219+
matching_bounds={:?}",
1220+
matching_bounds
12121221
);
1213-
matching_bound
1222+
matching_bounds
12141223
}
12151224

12161225
fn match_projection(
@@ -1299,14 +1308,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12991308
// clause so don't go around looking for impls.
13001309
!is_global(cand)
13011310
}
1302-
ObjectCandidate | ProjectionCandidate => {
1311+
ObjectCandidate | ProjectionCandidate(_) => {
13031312
// Arbitrarily give param candidates priority
13041313
// over projection and object candidates.
13051314
!is_global(cand)
13061315
}
13071316
ParamCandidate(..) => false,
13081317
},
1309-
ObjectCandidate | ProjectionCandidate => match victim.candidate {
1318+
ObjectCandidate | ProjectionCandidate(_) => match victim.candidate {
13101319
AutoImplCandidate(..) => {
13111320
bug!(
13121321
"default implementations shouldn't be recorded \
@@ -1323,10 +1332,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13231332
| BuiltinUnsizeCandidate
13241333
| BuiltinCandidate { .. }
13251334
| TraitAliasCandidate(..) => true,
1326-
ObjectCandidate | ProjectionCandidate => {
1327-
// Arbitrarily give param candidates priority
1328-
// over projection and object candidates.
1329-
true
1335+
ObjectCandidate | ProjectionCandidate(_) => {
1336+
// Shouldn't have both an object and projection candidate,
1337+
// nor multiple object candidates. Multiple projection
1338+
// candidates are ambiguous.
1339+
false
13301340
}
13311341
ParamCandidate(ref cand) => is_global(cand),
13321342
},
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Make sure that if there are multiple applicable bounds on a projection, we
2+
// consider them ambiguous. In this test we are initially trying to solve
3+
// `Self::Repr: From<_>`, which is ambiguous until we later infer `_` to
4+
// `{integer}`.
5+
6+
// check-pass
7+
8+
trait PrimeField: Sized {
9+
type Repr: From<u64> + From<Self>;
10+
type Repr2: From<Self> + From<u64>;
11+
12+
fn method() {
13+
Self::Repr::from(10);
14+
Self::Repr2::from(10);
15+
}
16+
}
17+
18+
fn function<T: PrimeField>() {
19+
T::Repr::from(10);
20+
T::Repr2::from(10);
21+
}
22+
23+
fn main() {}

0 commit comments

Comments
 (0)