Skip to content

Commit fb4e0a0

Browse files
committed
Implement trait upcasting coercion type-checking.
1 parent ac354cf commit fb4e0a0

File tree

10 files changed

+130
-25
lines changed

10 files changed

+130
-25
lines changed

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

+6-16
Original file line numberDiff line numberDiff line change
@@ -693,22 +693,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
693693
let may_apply = match (source.kind(), target.kind()) {
694694
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
695695
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
696-
// Upcasts permit two things:
697-
//
698-
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
699-
// 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
700-
//
701-
// Note that neither of these changes requires any
702-
// change at runtime. Eventually this will be
703-
// generalized.
704-
//
705-
// We always upcast when we can because of reason
706-
// #2 (region bounds).
707-
data_a.principal_def_id() == data_b.principal_def_id()
708-
&& data_b
709-
.auto_traits()
710-
// All of a's auto traits need to be in b's auto traits.
711-
.all(|b| data_a.auto_traits().any(|a| a == b))
696+
// See `confirm_builtin_unsize_candidate` for more info.
697+
let auto_traits_compatible = data_b
698+
.auto_traits()
699+
// All of a's auto traits need to be in b's auto traits.
700+
.all(|b| data_a.auto_traits().any(|a| a == b));
701+
auto_traits_compatible
712702
}
713703

714704
// `T` -> `Trait`

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

+50-4
Original file line numberDiff line numberDiff line change
@@ -703,10 +703,56 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
703703
match (source.kind(), target.kind()) {
704704
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
705705
(&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
706-
// See `assemble_candidates_for_unsizing` for more info.
707-
let iter = data_a
708-
.principal()
709-
.map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
706+
// Upcast coercions permit several things:
707+
//
708+
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
709+
// 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
710+
// 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar`
711+
//
712+
// Note that neither of the first two of these changes requires any
713+
// change at runtime. The third needs to change pointer metadata at runtime.
714+
//
715+
// We always perform upcasting coercions when we can because of reason
716+
// #2 (region bounds).
717+
718+
// We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`.
719+
720+
let principal_a = data_a.principal();
721+
let principal_def_id_b = data_b.principal_def_id();
722+
723+
let existential_predicate = if let Some(principal_a) = principal_a {
724+
let source_trait_ref = principal_a.with_self_ty(tcx, source);
725+
let target_trait_did = principal_def_id_b.ok_or_else(|| Unimplemented)?;
726+
let upcast_idx = util::supertraits(tcx, source_trait_ref)
727+
.position(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did)
728+
.ok_or_else(|| Unimplemented)?;
729+
// FIXME(crlf0710): This is less than ideal, for example,
730+
// if the trait is defined as `trait Foo: Bar<u32> + Bar<i32>`,
731+
// the coercion from Box<Foo> to Box<dyn Bar<_>> is actually ambiguous.
732+
// We currently make this coercion fail for now.
733+
//
734+
// see #65991 for more information.
735+
if util::supertraits(tcx, source_trait_ref)
736+
.skip(upcast_idx + 1)
737+
.any(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did)
738+
{
739+
return Err(Unimplemented);
740+
}
741+
let target_trait_ref =
742+
util::supertraits(tcx, source_trait_ref).nth(upcast_idx).unwrap();
743+
let existential_predicate = target_trait_ref.map_bound(|trait_ref| {
744+
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
745+
tcx, trait_ref,
746+
))
747+
});
748+
Some(existential_predicate)
749+
} else if principal_def_id_b.is_none() {
750+
None
751+
} else {
752+
return Err(Unimplemented);
753+
};
754+
755+
let iter = existential_predicate
710756
.into_iter()
711757
.chain(
712758
data_a

compiler/rustc_typeck/src/check/coercion.rs

+20
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
576576
)];
577577

578578
let mut has_unsized_tuple_coercion = false;
579+
let mut has_trait_upcasting_coercion = false;
579580

580581
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
581582
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
@@ -590,7 +591,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
590591
if traits.contains(&trait_pred.def_id()) =>
591592
{
592593
if unsize_did == trait_pred.def_id() {
594+
let self_ty = trait_pred.self_ty();
593595
let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
596+
if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
597+
(self_ty.kind(), unsize_ty.kind())
598+
{
599+
if data_a.principal_def_id() != data_b.principal_def_id() {
600+
debug!("coerce_unsized: found trait upcasting coercion");
601+
has_trait_upcasting_coercion = true;
602+
}
603+
}
594604
if let ty::Tuple(..) = unsize_ty.kind() {
595605
debug!("coerce_unsized: found unsized tuple coercion");
596606
has_unsized_tuple_coercion = true;
@@ -666,6 +676,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
666676
.emit();
667677
}
668678

679+
if has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting {
680+
feature_err(
681+
&self.tcx.sess.parse_sess,
682+
sym::trait_upcasting,
683+
self.cause.span,
684+
"trait upcasting coercion is experimental",
685+
)
686+
.emit();
687+
}
688+
669689
Ok(coercion)
670690
}
671691

src/test/ui/feature-gates/feature-gate-trait_upcasting.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ impl Bar for () {}
99
fn main() {
1010
let bar: &dyn Bar = &();
1111
let foo: &dyn Foo = bar;
12-
//~^ ERROR trait upcasting is experimental [E0658]
12+
//~^ ERROR trait upcasting coercion is experimental [E0658]
1313
}

src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0658]: trait upcasting is experimental
1+
error[E0658]: trait upcasting coercion is experimental
22
--> $DIR/feature-gate-trait_upcasting.rs:11:25
33
|
44
LL | let foo: &dyn Foo = bar;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#![feature(box_syntax)]
22

33
struct Test {
4-
func: Box<dyn FnMut() + 'static>
4+
func: Box<dyn FnMut() + 'static>,
55
}
66

77
fn main() {
88
let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
9-
let test = box Test { func: closure }; //~ ERROR trait upcasting is experimental [E0658]
9+
let test = box Test { func: closure }; //~ ERROR trait upcasting coercion is experimental [E0658]
1010
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0658]: trait upcasting is experimental
1+
error[E0658]: trait upcasting coercion is experimental
22
--> $DIR/issue-11515.rs:9:33
33
|
44
LL | let test = box Test { func: closure };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-pass
2+
#![feature(box_syntax, trait_upcasting)]
3+
#![allow(incomplete_features)]
4+
5+
struct Test {
6+
func: Box<dyn FnMut() + 'static>,
7+
}
8+
9+
fn main() {
10+
let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
11+
let mut test = box Test { func: closure };
12+
(test.func)();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// check-fail
2+
#![feature(trait_upcasting)]
3+
#![allow(incomplete_features)]
4+
5+
trait Bar<T> {
6+
fn bar(&self, _: T) {}
7+
}
8+
9+
trait Foo : Bar<i32> + Bar<u32> {
10+
fn foo(&self, _: ()) {}
11+
}
12+
13+
struct S;
14+
15+
impl Bar<i32> for S {}
16+
impl Bar<u32> for S {}
17+
impl Foo for S {}
18+
19+
fn main() {
20+
let s: &dyn Foo = &S;
21+
let t: &dyn Bar<_> = s; //~ ERROR mismatched types
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/multiple-occurence-ambiguousity.rs:21:26
3+
|
4+
LL | let t: &dyn Bar<_> = s;
5+
| ----------- ^ expected trait `Bar`, found trait `Foo`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected reference `&dyn Bar<_>`
10+
found reference `&dyn Foo`
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)