diff options
| author | bors <bors@rust-lang.org> | 2021-07-31 07:46:14 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-07-31 07:46:14 +0000 |
| commit | 7069a8c2b78c5d23205de1cabb4c2a65229dbd8f (patch) | |
| tree | e2afdc516470e471a88c40ce97494138dfa1c740 /compiler | |
| parent | 6b0b07d41f07e1ba5808693d900903499ccf7a32 (diff) | |
| parent | a28ee25483d92b7924752651d9c751ae2c4f2c34 (diff) | |
| download | rust-7069a8c2b78c5d23205de1cabb4c2a65229dbd8f.tar.gz rust-7069a8c2b78c5d23205de1cabb4c2a65229dbd8f.zip | |
Auto merge of #86264 - crlf0710:trait_upcasting_part1, r=nikomatsakis
Trait upcasting coercion (part1) This revives the first part of earlier PR #60900 . It's not very clear to me which parts of that pr was design decisions, so i decide to cut it into pieces and land them incrementally. This allows more eyes on the details. This is the first part, it adds feature gates, adds feature gates tests, and implemented the unsize conversion part. (I hope i have dealt with the `ExistentialTraitRef` values correctly...) The next part will be implementing the pointer casting.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_feature/src/active.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/select/confirmation.rs | 54 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/coercion.rs | 20 |
5 files changed, 81 insertions, 20 deletions
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1ec9a0518b8..638330c904d 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -683,6 +683,10 @@ declare_features! ( /// Allows the `?` operator in const contexts. (active, const_try, "1.56.0", Some(74935), None), + /// Allows upcasting trait objects via supertraits. + /// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. + (incomplete, trait_upcasting, "1.56.0", Some(65991), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 295e53aba35..1ac489f600a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1270,6 +1270,7 @@ symbols! { trace_macros, track_caller, trait_alias, + trait_upcasting, transmute, transparent, transparent_enums, 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<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 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) } |
