Skip to content

Commit 04e589c

Browse files
committed
Consider fewer predicates for projection candidates
We now require that projection candidates are applicable with the idenitity substs of the trait, rather than allowing predicates that are only applicable for certain substs.
1 parent 29272fc commit 04e589c

26 files changed

+666
-38
lines changed

src/librustc_middle/query/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ rustc_queries! {
133133
cache_on_disk_if { key.is_local() }
134134
}
135135

136+
/// Returns the list of predicates that can be used for
137+
/// `SelectionCandidate::ProjectionCandidate` and
138+
/// `ProjectionTyCandidate::TraitDef`.
139+
query projection_predicates(key: DefId) -> &'tcx ty::List<ty::Predicate<'tcx>> {
140+
desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) }
141+
}
142+
136143
query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
137144
desc { "looking up the native libraries of a linked crate" }
138145
}

src/librustc_middle/ty/subst.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,17 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
333333
/// in a different item, with `target_substs` as the base for
334334
/// the target impl/trait, with the source child-specific
335335
/// parameters (e.g., method parameters) on top of that base.
336+
///
337+
/// For example given:
338+
///
339+
/// trait X<S> { fn f<T>(); }
340+
/// impl<U> X<U> for U { fn f<V>() {} }
341+
///
342+
/// * If `self` is `[Self, S, T]`: the identity substs of `f` in the trait.
343+
/// * If `source_ancestor` is the def_id of the trait.
344+
/// * If `target_substs` is `[U]`, the substs for the impl.
345+
/// * Then we will return `[U, T]`, the subst for `f` in the impl that
346+
/// are needed for it to match the trait.
336347
pub fn rebase_onto(
337348
&self,
338349
tcx: TyCtxt<'tcx>,

src/librustc_trait_selection/traits/project.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -896,9 +896,12 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
896896

897897
let tcx = selcx.tcx();
898898
// Check whether the self-type is itself a projection.
899-
let (def_id, substs) = match obligation_trait_ref.self_ty().kind {
900-
ty::Projection(ref data) => (data.trait_ref(tcx).def_id, data.substs),
901-
ty::Opaque(def_id, substs) => (def_id, substs),
899+
// If so, extract what we know from the trait and try to come up with a good answer.
900+
let bounds = match obligation_trait_ref.self_ty().kind {
901+
ty::Projection(ref data) => {
902+
tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
903+
}
904+
ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs),
902905
ty::Infer(ty::TyVar(_)) => {
903906
// If the self-type is an inference variable, then it MAY wind up
904907
// being a projected type, so induce an ambiguity.
@@ -908,17 +911,13 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
908911
_ => return,
909912
};
910913

911-
// If so, extract what we know from the trait and try to come up with a good answer.
912-
let trait_predicates = tcx.predicates_of(def_id);
913-
let bounds = trait_predicates.instantiate(tcx, substs);
914-
let bounds = elaborate_predicates(tcx, bounds.predicates.into_iter()).map(|o| o.predicate);
915914
assemble_candidates_from_predicates(
916915
selcx,
917916
obligation,
918917
obligation_trait_ref,
919918
candidate_set,
920919
ProjectionTyCandidate::TraitDef,
921-
bounds,
920+
bounds.iter(),
922921
)
923922
}
924923

@@ -1484,6 +1483,12 @@ fn confirm_impl_candidate<'cx, 'tcx>(
14841483
);
14851484
return Progress { ty: tcx.ty_error(), obligations: nested };
14861485
}
1486+
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
1487+
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
1488+
//
1489+
// * `obligation.predicate.substs` is `[Vec<u32>, S]`
1490+
// * `substs` is `[u32]`
1491+
// * `substs` ends up as `[u32, S]`
14871492
let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
14881493
let substs =
14891494
translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node);

src/librustc_trait_selection/traits/select/mod.rs

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,9 +1273,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12731273
placeholder_trait_predicate,
12741274
);
12751275

1276-
let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind {
1277-
ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs),
1278-
ty::Opaque(def_id, substs) => (def_id, substs),
1276+
let tcx = self.infcx.tcx;
1277+
let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind {
1278+
ty::Projection(ref data) => {
1279+
tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
1280+
}
1281+
ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs),
12791282
_ => {
12801283
span_bug!(
12811284
obligation.cause.span,
@@ -1285,32 +1288,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12851288
);
12861289
}
12871290
};
1288-
debug!(
1289-
"match_projection_obligation_against_definition_bounds: \
1290-
def_id={:?}, substs={:?}",
1291-
def_id, substs
1292-
);
12931291

1294-
let predicates_of = self.tcx().predicates_of(def_id);
1295-
let bounds = predicates_of.instantiate(self.tcx(), substs);
1296-
debug!(
1297-
"match_projection_obligation_against_definition_bounds: \
1298-
bounds={:?}",
1299-
bounds
1300-
);
1301-
1302-
let elaborated_predicates =
1303-
util::elaborate_predicates(self.tcx(), bounds.predicates.into_iter());
1304-
let matching_bound = elaborated_predicates.filter_to_traits().find(|bound| {
1305-
self.infcx.probe(|_| {
1306-
self.match_projection(
1307-
obligation,
1308-
*bound,
1309-
placeholder_trait_predicate.trait_ref,
1310-
&placeholder_map,
1311-
snapshot,
1312-
)
1313-
})
1292+
let matching_bound = predicates.iter().find_map(|bound| {
1293+
if let ty::PredicateKind::Trait(bound, _) = bound.kind() {
1294+
let bound = bound.to_poly_trait_ref();
1295+
if self.infcx.probe(|_| {
1296+
self.match_projection(
1297+
obligation,
1298+
bound,
1299+
placeholder_trait_predicate.trait_ref,
1300+
&placeholder_map,
1301+
snapshot,
1302+
)
1303+
}) {
1304+
return Some(bound);
1305+
}
1306+
}
1307+
None
13141308
});
13151309

13161310
debug!(

src/librustc_ty/ty.rs

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use rustc_data_structures::svh::Svh;
22
use rustc_hir as hir;
3+
use rustc_hir::def::DefKind;
34
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
5+
use rustc_infer::traits::util;
46
use rustc_middle::hir::map as hir_map;
5-
use rustc_middle::ty::subst::Subst;
7+
use rustc_middle::ty::subst::{InternalSubsts, Subst};
68
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};
79
use rustc_session::CrateDisambiguator;
810
use rustc_span::symbol::Symbol;
@@ -365,6 +367,106 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
365367
fn_like.asyncness()
366368
}
367369

370+
/// For associated types we allow bounds written on the associated type
371+
/// (`type X: Trait`) to be used as candidates. We also allow the same bounds
372+
/// when desugared as bounds on the trait `where Self::X: Trait`.
373+
///
374+
/// Note that this filtering is done with the trait's identity substs to
375+
/// simplify checking that these bounds are met in impls. This means that
376+
/// a bound such as `for<'b> <Self as X<'b>>::U: Clone` can't be used, as in
377+
/// `hr-associated-type-bound-1.rs`.
378+
fn associated_type_projection_predicates(
379+
tcx: TyCtxt<'_>,
380+
def_id: DefId,
381+
) -> &'_ ty::List<ty::Predicate<'_>> {
382+
let trait_id = tcx.associated_item(def_id).container.id();
383+
let trait_substs = InternalSubsts::identity_for_item(tcx, trait_id);
384+
385+
let generic_trait_bounds = tcx.predicates_of(trait_id);
386+
let trait_bounds = generic_trait_bounds.instantiate_identity(tcx);
387+
let trait_predicates = util::elaborate_predicates(tcx, trait_bounds.predicates.into_iter());
388+
389+
let predicates = trait_predicates.filter_map(|obligation| {
390+
let pred = obligation.predicate;
391+
match pred.kind() {
392+
ty::PredicateKind::Trait(tr, _) => {
393+
if let ty::Projection(p) = tr.skip_binder().self_ty().kind {
394+
if p.item_def_id == def_id && p.substs.starts_with(trait_substs) {
395+
return Some(pred);
396+
}
397+
}
398+
}
399+
ty::PredicateKind::Projection(proj) => {
400+
if let ty::Projection(p) = proj.skip_binder().projection_ty.self_ty().kind {
401+
if p.item_def_id == def_id && p.substs.starts_with(trait_substs) {
402+
return Some(pred);
403+
}
404+
}
405+
}
406+
_ => {}
407+
}
408+
None
409+
});
410+
411+
let result = tcx.mk_predicates(predicates);
412+
debug!("associated_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result);
413+
result
414+
}
415+
416+
/// Opaque types don't have the same issues as associated types: the only
417+
/// predicates on an opaque type (excluding those it inherits from its parent
418+
/// item) should be of the form we're expecting.
419+
fn opaque_type_projection_predicates(
420+
tcx: TyCtxt<'_>,
421+
def_id: DefId,
422+
) -> &'_ ty::List<ty::Predicate<'_>> {
423+
let substs = InternalSubsts::identity_for_item(tcx, def_id);
424+
425+
let generics_bounds = tcx.predicates_of(def_id);
426+
let bounds = generics_bounds.instantiate_identity(tcx);
427+
let predicates = util::elaborate_predicates(tcx, bounds.predicates.into_iter());
428+
429+
let filtered_predicates = predicates.filter_map(|obligation| {
430+
let pred = obligation.predicate;
431+
match pred.kind() {
432+
ty::PredicateKind::Trait(tr, _) => {
433+
if let ty::Opaque(opaque_def_id, opaque_substs) = tr.skip_binder().self_ty().kind {
434+
if opaque_def_id == def_id && opaque_substs == substs {
435+
return Some(pred);
436+
}
437+
}
438+
}
439+
ty::PredicateKind::Projection(proj) => {
440+
if let ty::Opaque(opaque_def_id, opaque_substs) =
441+
proj.skip_binder().projection_ty.self_ty().kind
442+
{
443+
if opaque_def_id == def_id && opaque_substs == substs {
444+
return Some(pred);
445+
}
446+
}
447+
}
448+
_ => {}
449+
}
450+
tcx.sess.delay_span_bug(
451+
obligation.cause.span(tcx),
452+
&format!("unexpected predicate {:?} on opaque type", pred),
453+
);
454+
None
455+
});
456+
457+
let result = tcx.mk_predicates(filtered_predicates);
458+
debug!("opaque_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result);
459+
result
460+
}
461+
462+
fn projection_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List<ty::Predicate<'_>> {
463+
match tcx.def_kind(def_id) {
464+
DefKind::AssocTy => associated_type_projection_predicates(tcx, def_id),
465+
DefKind::OpaqueTy => opaque_type_projection_predicates(tcx, def_id),
466+
k => bug!("projection_predicates called on {}", k.descr(def_id)),
467+
}
468+
}
469+
368470
pub fn provide(providers: &mut ty::query::Providers<'_>) {
369471
*providers = ty::query::Providers {
370472
asyncness,
@@ -381,6 +483,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
381483
instance_def_size_estimate,
382484
issue33140_self_ty,
383485
impl_defaultness,
486+
projection_predicates,
384487
..*providers
385488
};
386489
}

src/librustc_typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2281,7 +2281,7 @@ fn check_impl_items_against_trait<'tcx>(
22812281
&ty_trait_item,
22822282
impl_trait_ref,
22832283
opt_trait_span,
2284-
)
2284+
);
22852285
} else {
22862286
let mut err = struct_span_err!(
22872287
tcx.sess,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
trait X<'a>
2+
where
3+
for<'b> <Self as X<'b>>::U: Clone,
4+
{
5+
type U: ?Sized;
6+
fn f(&self, x: &Self::U) {
7+
<Self::U>::clone(x);
8+
}
9+
}
10+
11+
impl X<'_> for i32 {
12+
type U = str;
13+
//~^ ERROR the trait bound `for<'b> <i32 as X<'b>>::U: std::clone::Clone`
14+
}
15+
16+
fn main() {
17+
1i32.f("abc");
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0277]: the trait bound `for<'b> <i32 as X<'b>>::U: std::clone::Clone` is not satisfied
2+
--> $DIR/hr-associated-type-bound-1.rs:12:14
3+
|
4+
LL | trait X<'a>
5+
| - required by a bound in this
6+
LL | where
7+
LL | for<'b> <Self as X<'b>>::U: Clone,
8+
| ----- required by this bound in `X`
9+
...
10+
LL | type U = str;
11+
| ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `<i32 as X<'b>>::U`
12+
|
13+
= help: the following implementations were found:
14+
<&T as std::clone::Clone>
15+
<&mut T as std::clone::Clone>
16+
17+
error: aborting due to previous error
18+
19+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
trait X<'a>
2+
where
3+
for<'b> <Self as X<'b>>::U: Clone,
4+
{
5+
type U: ?Sized;
6+
fn f(&self, x: &Self::U) {
7+
<Self::U>::clone(x);
8+
}
9+
}
10+
11+
impl X<'_> for u32
12+
where
13+
for<'b> <Self as X<'b>>::U: Clone,
14+
{
15+
type U = str;
16+
}
17+
18+
fn main() {
19+
1u32.f("abc");
20+
//~^ ERROR no method named `f` found for type `u32` in the current scope
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0599]: no method named `f` found for type `u32` in the current scope
2+
--> $DIR/hr-associated-type-bound-2.rs:19:10
3+
|
4+
LL | 1u32.f("abc");
5+
| ^ method not found in `u32`
6+
|
7+
= note: the method `f` exists but the following trait bounds were not satisfied:
8+
`<u32 as X<'b>>::U: std::clone::Clone`
9+
which is required by `u32: X`
10+
11+
error: aborting due to previous error
12+
13+
For more information about this error, try `rustc --explain E0599`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait X<'a>
2+
where
3+
for<'b> <Self as X<'b>>::U: Clone,
4+
{
5+
type U: ?Sized;
6+
}
7+
fn f<'a, T: X<'a> + ?Sized>(x: &<T as X<'a>>::U) {
8+
//~^ ERROR the trait bound `for<'b> <T as X<'b>>::U: std::clone::Clone` is not satisfied
9+
<<T as X<'_>>::U>::clone(x);
10+
}
11+
12+
pub fn main() {
13+
f::<dyn X<'_, U = str>>("abc");
14+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0277]: the trait bound `for<'b> <T as X<'b>>::U: std::clone::Clone` is not satisfied
2+
--> $DIR/hr-associated-type-bound-object.rs:7:13
3+
|
4+
LL | trait X<'a>
5+
| - required by a bound in this
6+
LL | where
7+
LL | for<'b> <Self as X<'b>>::U: Clone,
8+
| ----- required by this bound in `X`
9+
...
10+
LL | fn f<'a, T: X<'a> + ?Sized>(x: &<T as X<'a>>::U) {
11+
| ^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `<T as X<'b>>::U`
12+
|
13+
= help: the following implementations were found:
14+
<&T as std::clone::Clone>
15+
<&mut T as std::clone::Clone>
16+
17+
error: aborting due to previous error
18+
19+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)