Implement trait upcasting coercion type-checking.
This commit is contained in:
parent
ac354cf5ce
commit
fb4e0a0972
10 changed files with 130 additions and 25 deletions
|
@ -693,22 +693,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
let may_apply = match (source.kind(), target.kind()) {
|
||||
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
|
||||
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
|
||||
// Upcasts permit two things:
|
||||
//
|
||||
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
|
||||
// 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
|
||||
//
|
||||
// Note that neither of these changes requires any
|
||||
// change at runtime. Eventually this will be
|
||||
// generalized.
|
||||
//
|
||||
// We always upcast when we can because of reason
|
||||
// #2 (region bounds).
|
||||
data_a.principal_def_id() == data_b.principal_def_id()
|
||||
&& data_b
|
||||
.auto_traits()
|
||||
// All of a's auto traits need to be in b's auto traits.
|
||||
.all(|b| data_a.auto_traits().any(|a| a == b))
|
||||
// See `confirm_builtin_unsize_candidate` for more info.
|
||||
let auto_traits_compatible = data_b
|
||||
.auto_traits()
|
||||
// All of a's auto traits need to be in b's auto traits.
|
||||
.all(|b| data_a.auto_traits().any(|a| a == b));
|
||||
auto_traits_compatible
|
||||
}
|
||||
|
||||
// `T` -> `Trait`
|
||||
|
|
|
@ -703,10 +703,56 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
match (source.kind(), target.kind()) {
|
||||
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
|
||||
(&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
|
||||
// See `assemble_candidates_for_unsizing` for more info.
|
||||
let iter = data_a
|
||||
.principal()
|
||||
.map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
|
||||
// Upcast coercions permit several things:
|
||||
//
|
||||
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
|
||||
// 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
|
||||
// 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar`
|
||||
//
|
||||
// Note that neither of the first two of these changes requires any
|
||||
// change at runtime. The third needs to change pointer metadata at runtime.
|
||||
//
|
||||
// We always perform upcasting coercions when we can because of reason
|
||||
// #2 (region bounds).
|
||||
|
||||
// We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`.
|
||||
|
||||
let principal_a = data_a.principal();
|
||||
let principal_def_id_b = data_b.principal_def_id();
|
||||
|
||||
let existential_predicate = if let Some(principal_a) = principal_a {
|
||||
let source_trait_ref = principal_a.with_self_ty(tcx, source);
|
||||
let target_trait_did = principal_def_id_b.ok_or_else(|| Unimplemented)?;
|
||||
let upcast_idx = util::supertraits(tcx, source_trait_ref)
|
||||
.position(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did)
|
||||
.ok_or_else(|| Unimplemented)?;
|
||||
// FIXME(crlf0710): This is less than ideal, for example,
|
||||
// if the trait is defined as `trait Foo: Bar<u32> + Bar<i32>`,
|
||||
// the coercion from Box<Foo> to Box<dyn Bar<_>> is actually ambiguous.
|
||||
// We currently make this coercion fail for now.
|
||||
//
|
||||
// see #65991 for more information.
|
||||
if util::supertraits(tcx, source_trait_ref)
|
||||
.skip(upcast_idx + 1)
|
||||
.any(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did)
|
||||
{
|
||||
return Err(Unimplemented);
|
||||
}
|
||||
let target_trait_ref =
|
||||
util::supertraits(tcx, source_trait_ref).nth(upcast_idx).unwrap();
|
||||
let existential_predicate = target_trait_ref.map_bound(|trait_ref| {
|
||||
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
|
||||
tcx, trait_ref,
|
||||
))
|
||||
});
|
||||
Some(existential_predicate)
|
||||
} else if principal_def_id_b.is_none() {
|
||||
None
|
||||
} else {
|
||||
return Err(Unimplemented);
|
||||
};
|
||||
|
||||
let iter = existential_predicate
|
||||
.into_iter()
|
||||
.chain(
|
||||
data_a
|
||||
|
|
|
@ -576,6 +576,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
)];
|
||||
|
||||
let mut has_unsized_tuple_coercion = false;
|
||||
let mut has_trait_upcasting_coercion = false;
|
||||
|
||||
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
|
||||
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
|
||||
|
@ -590,7 +591,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
if traits.contains(&trait_pred.def_id()) =>
|
||||
{
|
||||
if unsize_did == trait_pred.def_id() {
|
||||
let self_ty = trait_pred.self_ty();
|
||||
let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
|
||||
if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
|
||||
(self_ty.kind(), unsize_ty.kind())
|
||||
{
|
||||
if data_a.principal_def_id() != data_b.principal_def_id() {
|
||||
debug!("coerce_unsized: found trait upcasting coercion");
|
||||
has_trait_upcasting_coercion = true;
|
||||
}
|
||||
}
|
||||
if let ty::Tuple(..) = unsize_ty.kind() {
|
||||
debug!("coerce_unsized: found unsized tuple coercion");
|
||||
has_unsized_tuple_coercion = true;
|
||||
|
@ -666,6 +676,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
.emit();
|
||||
}
|
||||
|
||||
if has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting {
|
||||
feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
sym::trait_upcasting,
|
||||
self.cause.span,
|
||||
"trait upcasting coercion is experimental",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
Ok(coercion)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ impl Bar for () {}
|
|||
fn main() {
|
||||
let bar: &dyn Bar = &();
|
||||
let foo: &dyn Foo = bar;
|
||||
//~^ ERROR trait upcasting is experimental [E0658]
|
||||
//~^ ERROR trait upcasting coercion is experimental [E0658]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error[E0658]: trait upcasting is experimental
|
||||
error[E0658]: trait upcasting coercion is experimental
|
||||
--> $DIR/feature-gate-trait_upcasting.rs:11:25
|
||||
|
|
||||
LL | let foo: &dyn Foo = bar;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#![feature(box_syntax)]
|
||||
|
||||
struct Test {
|
||||
func: Box<dyn FnMut() + 'static>
|
||||
func: Box<dyn FnMut() + 'static>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
|
||||
let test = box Test { func: closure }; //~ ERROR trait upcasting is experimental [E0658]
|
||||
let test = box Test { func: closure }; //~ ERROR trait upcasting coercion is experimental [E0658]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error[E0658]: trait upcasting is experimental
|
||||
error[E0658]: trait upcasting coercion is experimental
|
||||
--> $DIR/issue-11515.rs:9:33
|
||||
|
|
||||
LL | let test = box Test { func: closure };
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// run-pass
|
||||
#![feature(box_syntax, trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Test {
|
||||
func: Box<dyn FnMut() + 'static>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
|
||||
let mut test = box Test { func: closure };
|
||||
(test.func)();
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// check-fail
|
||||
#![feature(trait_upcasting)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Bar<T> {
|
||||
fn bar(&self, _: T) {}
|
||||
}
|
||||
|
||||
trait Foo : Bar<i32> + Bar<u32> {
|
||||
fn foo(&self, _: ()) {}
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
impl Bar<i32> for S {}
|
||||
impl Bar<u32> for S {}
|
||||
impl Foo for S {}
|
||||
|
||||
fn main() {
|
||||
let s: &dyn Foo = &S;
|
||||
let t: &dyn Bar<_> = s; //~ ERROR mismatched types
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/multiple-occurence-ambiguousity.rs:21:26
|
||||
|
|
||||
LL | let t: &dyn Bar<_> = s;
|
||||
| ----------- ^ expected trait `Bar`, found trait `Foo`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected reference `&dyn Bar<_>`
|
||||
found reference `&dyn Foo`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Reference in a new issue