Skip to content

Commit d14ee0b

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 c920ae9 commit d14ee0b

26 files changed

+663
-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
@@ -895,9 +895,12 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
895895

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

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

@@ -1474,6 +1473,12 @@ fn confirm_impl_candidate<'cx, 'tcx>(
14741473
);
14751474
return Progress { ty: tcx.types.err, obligations: nested };
14761475
}
1476+
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
1477+
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
1478+
//
1479+
// * `obligation.predicate.substs` is `[Vec<u32>, S]`
1480+
// * `substs` is `[u32]`
1481+
// * `substs` ends up as `[u32, S]`
14771482
let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
14781483
let substs =
14791484
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
@@ -1265,9 +1265,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12651265
placeholder_trait_predicate,
12661266
);
12671267

1268-
let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind {
1269-
ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs),
1270-
ty::Opaque(def_id, substs) => (def_id, substs),
1268+
let tcx = self.infcx.tcx;
1269+
let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind {
1270+
ty::Projection(ref data) => {
1271+
tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs)
1272+
}
1273+
ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs),
12711274
_ => {
12721275
span_bug!(
12731276
obligation.cause.span,
@@ -1277,32 +1280,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12771280
);
12781281
}
12791282
};
1280-
debug!(
1281-
"match_projection_obligation_against_definition_bounds: \
1282-
def_id={:?}, substs={:?}",
1283-
def_id, substs
1284-
);
12851283

1286-
let predicates_of = self.tcx().predicates_of(def_id);
1287-
let bounds = predicates_of.instantiate(self.tcx(), substs);
1288-
debug!(
1289-
"match_projection_obligation_against_definition_bounds: \
1290-
bounds={:?}",
1291-
bounds
1292-
);
1293-
1294-
let elaborated_predicates =
1295-
util::elaborate_predicates(self.tcx(), bounds.predicates.into_iter());
1296-
let matching_bound = elaborated_predicates.filter_to_traits().find(|bound| {
1297-
self.infcx.probe(|_| {
1298-
self.match_projection(
1299-
obligation,
1300-
*bound,
1301-
placeholder_trait_predicate.trait_ref,
1302-
&placeholder_map,
1303-
snapshot,
1304-
)
1305-
})
1284+
let matching_bound = predicates.iter().find_map(|bound| {
1285+
if let ty::PredicateKind::Trait(bound, _) = bound.kind() {
1286+
let bound = bound.to_poly_trait_ref();
1287+
if self.infcx.probe(|_| {
1288+
self.match_projection(
1289+
obligation,
1290+
bound,
1291+
placeholder_trait_predicate.trait_ref,
1292+
&placeholder_map,
1293+
snapshot,
1294+
)
1295+
}) {
1296+
return Some(bound);
1297+
}
1298+
}
1299+
None
13061300
});
13071301

13081302
debug!(

src/librustc_ty/ty.rs

Lines changed: 101 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;
@@ -367,6 +369,103 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
367369
fn_like.asyncness()
368370
}
369371

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

src/librustc_typeck/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2124,7 +2124,7 @@ fn check_impl_items_against_trait<'tcx>(
21242124
&ty_trait_item,
21252125
impl_trait_ref,
21262126
opt_trait_span,
2127-
)
2127+
);
21282128
} else {
21292129
let mut err = struct_span_err!(
21302130
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)