Skip to content

Commit 2bdf723

Browse files
committed
Ensure that associated types for trait objects satisfy their bounds
1 parent 0a76584 commit 2bdf723

10 files changed

+166
-18
lines changed

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

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use rustc_data_structures::stack::ensure_sufficient_stack;
1010
use rustc_hir::lang_items::LangItem;
1111
use rustc_index::bit_set::GrowableBitSet;
12-
use rustc_infer::infer::InferOk;
12+
use rustc_infer::infer::{self, InferOk};
1313
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
1414
use rustc_middle::ty::{self, Ty};
1515
use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness};
@@ -342,21 +342,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
342342
) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> {
343343
debug!("confirm_object_candidate({:?})", obligation);
344344

345-
// FIXME(nmatsakis) skipping binder here seems wrong -- we should
346-
// probably flatten the binder from the obligation and the binder
347-
// from the object. Have to try to make a broken test case that
348-
// results.
349-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
350-
let poly_trait_ref = match self_ty.kind() {
351-
ty::Dynamic(data, ..) => data
352-
.principal()
353-
.unwrap_or_else(|| {
354-
span_bug!(obligation.cause.span, "object candidate with no principal")
355-
})
356-
.with_self_ty(self.tcx(), self_ty),
345+
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&obligation.self_ty());
346+
let data = match self_ty.kind() {
347+
ty::Dynamic(data, ..) => data,
357348
_ => span_bug!(obligation.cause.span, "object candidate with non-object"),
358349
};
359350

351+
let poly_trait_ref = data
352+
.principal()
353+
.unwrap_or_else(|| {
354+
span_bug!(obligation.cause.span, "object candidate with no principal")
355+
})
356+
.with_self_ty(self.tcx(), self_ty);
357+
360358
let mut upcast_trait_ref = None;
361359
let mut nested = vec![];
362360
let vtable_base;
@@ -388,6 +386,46 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
388386
vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum();
389387
}
390388

389+
for bound in data.skip_binder() {
390+
match bound {
391+
ty::ExistentialPredicate::Projection(projection) => {
392+
// This maybe belongs in wf, but that can't (doesn't) handle
393+
// higher-ranked things.
394+
// Prevent, e.g., `dyn Iterator<Item = str>`.
395+
// FIXME(generic_associated_types): We need some way to
396+
// ensure that for `dyn for<'a> X<Item<'a> = &'a ()>` the
397+
// bound holds for all `'a`.
398+
let (infer_projection, _) = self.infcx.replace_bound_vars_with_fresh_vars(
399+
obligation.cause.span,
400+
infer::HigherRankedType,
401+
&ty::Binder::bind(projection),
402+
);
403+
let substs: Vec<_> =
404+
iter::once(self_ty.into()).chain(infer_projection.substs).collect();
405+
let bounds =
406+
self.tcx().item_bounds(projection.item_def_id).iter().map(|bound| {
407+
// In the example above, `bound` is `<Self as Iterator>::Item: Sized`
408+
// `subst_bound` is `str: Sized`.
409+
let subst_bound = util::subst_assoc_item_bound(
410+
self.tcx(),
411+
bound,
412+
infer_projection.ty,
413+
&substs,
414+
);
415+
Obligation::new(
416+
obligation.cause.clone(),
417+
obligation.param_env.clone(),
418+
subst_bound,
419+
)
420+
});
421+
debug!("confirm_object_candidate: adding bounds: {:?}", bounds);
422+
nested.extend(bounds);
423+
}
424+
ty::ExistentialPredicate::Trait(_) | ty::ExistentialPredicate::AutoTrait(_) => {}
425+
}
426+
}
427+
428+
debug!("confirm_object_candidate: nested: {:?}", nested);
391429
ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested }
392430
}
393431

src/test/ui/issues/issue-41139.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ trait Trait {}
33
fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { panic!("") }
44

55
fn main() {
6+
// This isn't great. The issue here is that `dyn Trait` is not sized, so
7+
// `dyn Fn() -> dyn Trait` is not well-formed.
68
let t : &dyn Trait = &get_function()();
7-
//~^ ERROR cannot move a value of type dyn Trait
9+
//~^ ERROR expected function, found `&dyn std::ops::Fn() -> (dyn Trait + 'static)`
810
}

src/test/ui/issues/issue-41139.stderr

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
error[E0161]: cannot move a value of type dyn Trait: the size of dyn Trait cannot be statically determined
2-
--> $DIR/issue-41139.rs:6:27
1+
error[E0618]: expected function, found `&dyn std::ops::Fn() -> (dyn Trait + 'static)`
2+
--> $DIR/issue-41139.rs:8:27
33
|
4+
LL | fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { panic!("") }
5+
| ----------------------------------------------------------------- `get_function` defined here returns `&dyn std::ops::Fn() -> (dyn Trait + 'static)`
6+
...
47
LL | let t : &dyn Trait = &get_function()();
5-
| ^^^^^^^^^^^^^^^^
8+
| ^^^^^^^^^^^^^^--
9+
| |
10+
| call expression requires function
611

712
error: aborting due to previous error
813

9-
For more information about this error, try `rustc --explain E0161`.
14+
For more information about this error, try `rustc --explain E0618`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Check that we validate associated type bounds for trait objects
2+
3+
trait X {
4+
type Y: Clone;
5+
}
6+
7+
fn f<T: X + ?Sized>() {
8+
None::<T::Y>.clone();
9+
}
10+
11+
fn main() {
12+
f::<dyn X<Y = str>>();
13+
//~^ ERROR the trait bound `str: std::clone::Clone` is not satisfied
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied
2+
--> $DIR/check-trait-object-bounds-1.rs:12:5
3+
|
4+
LL | fn f<T: X + ?Sized>() {
5+
| - required by this bound in `f`
6+
...
7+
LL | f::<dyn X<Y = str>>();
8+
| ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Make sure that we're handling bound lifetimes correctly when validating trait
2+
// bounds.
3+
// run-pass
4+
5+
trait X<'a> {
6+
type F: FnOnce(&i32) -> &'a i32;
7+
}
8+
9+
fn f<T: for<'r> X<'r> + ?Sized>() {
10+
None::<T::F>.map(|f| f(&0));
11+
}
12+
13+
fn main() {
14+
f::<dyn for<'x> X<'x, F = fn(&i32) -> &'x i32>>();
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Check that we validate associated type bounds for trait objects when they
2+
// have bound lifetimes
3+
4+
trait X<'a> {
5+
type F: FnOnce(&i32) -> &'a i32;
6+
}
7+
8+
fn f<T: for<'r> X<'r> + ?Sized>() {
9+
None::<T::F>.map(|f| f(&0));
10+
}
11+
12+
fn main() {
13+
f::<dyn for<'x> X<'x, F = i32>>();
14+
//~^ expected a `std::ops::FnOnce<(&i32,)>` closure, found `i32`
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0277]: expected a `std::ops::FnOnce<(&i32,)>` closure, found `i32`
2+
--> $DIR/check-trait-object-bounds-2.rs:13:5
3+
|
4+
LL | fn f<T: for<'r> X<'r> + ?Sized>() {
5+
| ------------- required by this bound in `f`
6+
...
7+
LL | f::<dyn for<'x> X<'x, F = i32>>();
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(&i32,)>` closure, found `i32`
9+
|
10+
= help: the trait `for<'r> std::ops::FnOnce<(&'r i32,)>` is not implemented for `i32`
11+
12+
error: aborting due to previous error
13+
14+
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+
// Check that we validate associated type bounds for trait objects
2+
3+
trait X<'a> {
4+
type Y: Into<&'static str> + From<&'a str>;
5+
}
6+
7+
fn f<'a, T: X<'a> + ?Sized>(s: &'a str) -> &'static str {
8+
T::Y::from(s).into()
9+
}
10+
11+
pub fn main() {
12+
let z;
13+
{
14+
let s = String::from("abcdef");
15+
z = f::<dyn X<Y = &str>>(&s);
16+
//~^ ERROR `s` does not live long enough
17+
}
18+
19+
println!("{}", z)
20+
}
21+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied
2+
--> $DIR/check-trait-object-bounds-3.rs:12:5
3+
|
4+
LL | fn f<T: X + ?Sized>() {
5+
| - required by this bound in `f`
6+
...
7+
LL | f::<dyn X<Y = str>>();
8+
| ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)