diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 752f6a8debc..c7bf1f2a943 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -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` diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index f8297ee3a07..0c2099593a2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -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 + Bar`, + // the coercion from Box to Box> 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 diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index ba76b9c8dd5..a83b39a1108 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -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) } diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs index 0b702ab99fd..e4102f1cfa7 100644 --- a/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs +++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs @@ -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] } diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr index fa273926fbd..bc13a5d7d7b 100644 --- a/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr +++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr @@ -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; diff --git a/src/test/ui/issues/issue-11515.rs b/src/test/ui/issues/issue-11515.rs index 46a11002e51..2072f9c47e2 100644 --- a/src/test/ui/issues/issue-11515.rs +++ b/src/test/ui/issues/issue-11515.rs @@ -1,10 +1,10 @@ #![feature(box_syntax)] struct Test { - func: Box + func: Box, } fn main() { let closure: Box = 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] } diff --git a/src/test/ui/issues/issue-11515.stderr b/src/test/ui/issues/issue-11515.stderr index 708e3164117..a70e7c416bc 100644 --- a/src/test/ui/issues/issue-11515.stderr +++ b/src/test/ui/issues/issue-11515.stderr @@ -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 }; diff --git a/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs new file mode 100644 index 00000000000..277d9eabe4f --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs @@ -0,0 +1,13 @@ +// run-pass +#![feature(box_syntax, trait_upcasting)] +#![allow(incomplete_features)] + +struct Test { + func: Box, +} + +fn main() { + let closure: Box = Box::new(|| ()); + let mut test = box Test { func: closure }; + (test.func)(); +} diff --git a/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs new file mode 100644 index 00000000000..6986ad62172 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs @@ -0,0 +1,22 @@ +// check-fail +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Bar { + fn bar(&self, _: T) {} +} + +trait Foo : Bar + Bar { + fn foo(&self, _: ()) {} +} + +struct S; + +impl Bar for S {} +impl Bar for S {} +impl Foo for S {} + +fn main() { + let s: &dyn Foo = &S; + let t: &dyn Bar<_> = s; //~ ERROR mismatched types +} diff --git a/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr new file mode 100644 index 00000000000..e9670ad7def --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr @@ -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`.