diff options
| author | Charles Lew <crlf0710@gmail.com> | 2021-07-25 18:43:48 +0800 |
|---|---|---|
| committer | Charles Lew <crlf0710@gmail.com> | 2021-07-31 00:51:39 +0800 |
| commit | fb4e0a097238c309220d97d1a62350bafd953083 (patch) | |
| tree | bce243f13013ceddb82efd2f5ebc0b60445c55a2 /compiler/rustc_trait_selection/src/traits/select | |
| parent | ac354cf5ced544e7e7c74f67ea5fd82d5b501aed (diff) | |
Implement trait upcasting coercion type-checking.
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/select')
| -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 |
2 files changed, 56 insertions, 20 deletions
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 |
