Ensure that associated types for trait objects satisfy their bounds

This commit is contained in:
Matthew Jasper 2020-06-28 20:34:56 +01:00
parent 0a76584dcc
commit 2bdf723da7
10 changed files with 166 additions and 18 deletions

View File

@ -9,7 +9,7 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_index::bit_set::GrowableBitSet;
use rustc_infer::infer::InferOk;
use rustc_infer::infer::{self, InferOk};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness};
@ -342,21 +342,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> {
debug!("confirm_object_candidate({:?})", obligation);
// FIXME(nmatsakis) skipping binder here seems wrong -- we should
// probably flatten the binder from the obligation and the binder
// from the object. Have to try to make a broken test case that
// results.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let poly_trait_ref = match self_ty.kind() {
ty::Dynamic(data, ..) => data
.principal()
.unwrap_or_else(|| {
span_bug!(obligation.cause.span, "object candidate with no principal")
})
.with_self_ty(self.tcx(), self_ty),
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&obligation.self_ty());
let data = match self_ty.kind() {
ty::Dynamic(data, ..) => data,
_ => span_bug!(obligation.cause.span, "object candidate with non-object"),
};
let poly_trait_ref = data
.principal()
.unwrap_or_else(|| {
span_bug!(obligation.cause.span, "object candidate with no principal")
})
.with_self_ty(self.tcx(), self_ty);
let mut upcast_trait_ref = None;
let mut nested = vec![];
let vtable_base;
@ -388,6 +386,46 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum();
}
for bound in data.skip_binder() {
match bound {
ty::ExistentialPredicate::Projection(projection) => {
// This maybe belongs in wf, but that can't (doesn't) handle
// higher-ranked things.
// Prevent, e.g., `dyn Iterator<Item = str>`.
// FIXME(generic_associated_types): We need some way to
// ensure that for `dyn for<'a> X<Item<'a> = &'a ()>` the
// bound holds for all `'a`.
let (infer_projection, _) = self.infcx.replace_bound_vars_with_fresh_vars(
obligation.cause.span,
infer::HigherRankedType,
&ty::Binder::bind(projection),
);
let substs: Vec<_> =
iter::once(self_ty.into()).chain(infer_projection.substs).collect();
let bounds =
self.tcx().item_bounds(projection.item_def_id).iter().map(|bound| {
// In the example above, `bound` is `<Self as Iterator>::Item: Sized`
// `subst_bound` is `str: Sized`.
let subst_bound = util::subst_assoc_item_bound(
self.tcx(),
bound,
infer_projection.ty,
&substs,
);
Obligation::new(
obligation.cause.clone(),
obligation.param_env.clone(),
subst_bound,
)
});
debug!("confirm_object_candidate: adding bounds: {:?}", bounds);
nested.extend(bounds);
}
ty::ExistentialPredicate::Trait(_) | ty::ExistentialPredicate::AutoTrait(_) => {}
}
}
debug!("confirm_object_candidate: nested: {:?}", nested);
ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested }
}

View File

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

View File

@ -1,9 +1,14 @@
error[E0161]: cannot move a value of type dyn Trait: the size of dyn Trait cannot be statically determined
--> $DIR/issue-41139.rs:6:27
error[E0618]: expected function, found `&dyn std::ops::Fn() -> (dyn Trait + 'static)`
--> $DIR/issue-41139.rs:8:27
|
LL | fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { panic!("") }
| ----------------------------------------------------------------- `get_function` defined here returns `&dyn std::ops::Fn() -> (dyn Trait + 'static)`
...
LL | let t : &dyn Trait = &get_function()();
| ^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^--
| |
| call expression requires function
error: aborting due to previous error
For more information about this error, try `rustc --explain E0161`.
For more information about this error, try `rustc --explain E0618`.

View File

@ -0,0 +1,14 @@
// Check that we validate associated type bounds for trait objects
trait X {
type Y: Clone;
}
fn f<T: X + ?Sized>() {
None::<T::Y>.clone();
}
fn main() {
f::<dyn X<Y = str>>();
//~^ ERROR the trait bound `str: std::clone::Clone` is not satisfied
}

View File

@ -0,0 +1,12 @@
error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied
--> $DIR/check-trait-object-bounds-1.rs:12:5
|
LL | fn f<T: X + ?Sized>() {
| - required by this bound in `f`
...
LL | f::<dyn X<Y = str>>();
| ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,15 @@
// Make sure that we're handling bound lifetimes correctly when validating trait
// bounds.
// run-pass
trait X<'a> {
type F: FnOnce(&i32) -> &'a i32;
}
fn f<T: for<'r> X<'r> + ?Sized>() {
None::<T::F>.map(|f| f(&0));
}
fn main() {
f::<dyn for<'x> X<'x, F = fn(&i32) -> &'x i32>>();
}

View File

@ -0,0 +1,15 @@
// Check that we validate associated type bounds for trait objects when they
// have bound lifetimes
trait X<'a> {
type F: FnOnce(&i32) -> &'a i32;
}
fn f<T: for<'r> X<'r> + ?Sized>() {
None::<T::F>.map(|f| f(&0));
}
fn main() {
f::<dyn for<'x> X<'x, F = i32>>();
//~^ expected a `std::ops::FnOnce<(&i32,)>` closure, found `i32`
}

View File

@ -0,0 +1,14 @@
error[E0277]: expected a `std::ops::FnOnce<(&i32,)>` closure, found `i32`
--> $DIR/check-trait-object-bounds-2.rs:13:5
|
LL | fn f<T: for<'r> X<'r> + ?Sized>() {
| ------------- required by this bound in `f`
...
LL | f::<dyn for<'x> X<'x, F = i32>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(&i32,)>` closure, found `i32`
|
= help: the trait `for<'r> std::ops::FnOnce<(&'r i32,)>` is not implemented for `i32`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,21 @@
// Check that we validate associated type bounds for trait objects
trait X<'a> {
type Y: Into<&'static str> + From<&'a str>;
}
fn f<'a, T: X<'a> + ?Sized>(s: &'a str) -> &'static str {
T::Y::from(s).into()
}
pub fn main() {
let z;
{
let s = String::from("abcdef");
z = f::<dyn X<Y = &str>>(&s);
//~^ ERROR `s` does not live long enough
}
println!("{}", z)
}

View File

@ -0,0 +1,12 @@
error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied
--> $DIR/check-trait-object-bounds-3.rs:12:5
|
LL | fn f<T: X + ?Sized>() {
| - required by this bound in `f`
...
LL | f::<dyn X<Y = str>>();
| ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.