Skip to content

Commit d3e2578

Browse files
committedAug 21, 2021
Auto merge of #88135 - crlf0710:trait_upcasting_part_3, r=nikomatsakis
Trait upcasting coercion (part 3) By using separate candidates for each possible choice, this fixes type-checking issues in previous commits. r? `@nikomatsakis`
·
1.89.01.56.0
2 parents b1928aa + c22dfab commit d3e2578

File tree

19 files changed

+324
-239
lines changed

19 files changed

+324
-239
lines changed
 

‎compiler/rustc_codegen_cranelift/src/unsize.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,10 @@ pub(crate) fn unsized_info<'tcx>(
3131
if data_a.principal_def_id() == data_b.principal_def_id() {
3232
return old_info;
3333
}
34-
// trait upcasting coercion
3534

36-
// if both of the two `principal`s are `None`, this function would have returned early above.
37-
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
38-
let principal_a = data_a
39-
.principal()
40-
.expect("unsized_info: missing principal trait for trait upcasting coercion");
41-
let principal_b = data_b
42-
.principal()
43-
.expect("unsized_info: missing principal trait for trait upcasting coercion");
44-
45-
let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
46-
principal_a.with_self_ty(fx.tcx, source),
47-
principal_b.with_self_ty(fx.tcx, source),
48-
));
35+
// trait upcasting coercion
36+
let vptr_entry_idx =
37+
fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
4938

5039
if let Some(entry_idx) = vptr_entry_idx {
5140
let entry_idx = u32::try_from(entry_idx).unwrap();

‎compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,8 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
150150

151151
// trait upcasting coercion
152152

153-
// if both of the two `principal`s are `None`, this function would have returned early above.
154-
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
155-
let principal_a = data_a
156-
.principal()
157-
.expect("unsized_info: missing principal trait for trait upcasting coercion");
158-
let principal_b = data_b
159-
.principal()
160-
.expect("unsized_info: missing principal trait for trait upcasting coercion");
161-
162-
let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((
163-
principal_a.with_self_ty(cx.tcx(), source),
164-
principal_b.with_self_ty(cx.tcx(), source),
165-
));
153+
let vptr_entry_idx =
154+
cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
166155

167156
if let Some(entry_idx) = vptr_entry_idx {
168157
let ptr_ty = cx.type_i8p();

‎compiler/rustc_middle/src/query/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -987,9 +987,9 @@ rustc_queries! {
987987
desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) }
988988
}
989989

990-
query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option<usize> {
991-
desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable",
992-
tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) }
990+
query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::Ty<'tcx>, ty::Ty<'tcx>)) -> Option<usize> {
991+
desc { |tcx| "finding the slot within vtable for trait object {} vtable ptr during trait upcasting coercion from {} vtable",
992+
key.1, key.0 }
993993
}
994994

995995
query codegen_fulfill_obligation(

‎compiler/rustc_middle/src/traits/mod.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ pub enum ImplSource<'tcx, N> {
505505
/// Successful resolution for a builtin trait.
506506
Builtin(ImplSourceBuiltinData<N>),
507507

508+
/// ImplSource for trait upcasting coercion
509+
TraitUpcasting(ImplSourceTraitUpcastingData<'tcx, N>),
510+
508511
/// ImplSource automatically generated for a closure. The `DefId` is the ID
509512
/// of the closure expression. This is a `ImplSource::UserDefined` in spirit, but the
510513
/// impl is generated by the compiler and does not appear in the source.
@@ -540,6 +543,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
540543
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
541544
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
542545
ImplSource::TraitAlias(d) => d.nested,
546+
ImplSource::TraitUpcasting(d) => d.nested,
543547
}
544548
}
545549

@@ -556,6 +560,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
556560
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
557561
| ImplSource::Pointee(ImplSourcePointeeData) => &[],
558562
ImplSource::TraitAlias(d) => &d.nested[..],
563+
ImplSource::TraitUpcasting(d) => &d.nested[..],
559564
}
560565
}
561566

@@ -607,6 +612,13 @@ impl<'tcx, N> ImplSource<'tcx, N> {
607612
substs: d.substs,
608613
nested: d.nested.into_iter().map(f).collect(),
609614
}),
615+
ImplSource::TraitUpcasting(d) => {
616+
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData {
617+
upcast_trait_ref: d.upcast_trait_ref,
618+
vtable_vptr_slot: d.vtable_vptr_slot,
619+
nested: d.nested.into_iter().map(f).collect(),
620+
})
621+
}
610622
}
611623
}
612624
}
@@ -652,6 +664,20 @@ pub struct ImplSourceAutoImplData<N> {
652664
pub nested: Vec<N>,
653665
}
654666

667+
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
668+
pub struct ImplSourceTraitUpcastingData<'tcx, N> {
669+
/// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
670+
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
671+
672+
/// The vtable is formed by concatenating together the method lists of
673+
/// the base object trait and all supertraits, pointers to supertrait vtable will
674+
/// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable
675+
/// within that vtable.
676+
pub vtable_vptr_slot: Option<usize>,
677+
678+
pub nested: Vec<N>,
679+
}
680+
655681
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
656682
pub struct ImplSourceBuiltinData<N> {
657683
pub nested: Vec<N>,
@@ -663,8 +689,9 @@ pub struct ImplSourceObjectData<'tcx, N> {
663689
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
664690

665691
/// The vtable is formed by concatenating together the method lists of
666-
/// the base object trait and all supertraits; this is the start of
667-
/// `upcast_trait_ref`'s methods in that vtable.
692+
/// the base object trait and all supertraits, pointers to supertrait vtable will
693+
/// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
694+
/// in that vtable.
668695
pub vtable_base: usize,
669696

670697
pub nested: Vec<N>,

‎compiler/rustc_middle/src/traits/select.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ pub enum SelectionCandidate<'tcx> {
135135
/// `rustc_infer::traits::util::supertraits`.
136136
ObjectCandidate(usize),
137137

138+
/// Perform trait upcasting coercion of `dyn Trait` to a supertrait of `Trait`.
139+
/// The index is the position in the iterator returned by
140+
/// `rustc_infer::traits::util::supertraits`.
141+
TraitUpcastingUnsizeCandidate(usize),
142+
138143
BuiltinObjectCandidate,
139144

140145
BuiltinUnsizeCandidate,

‎compiler/rustc_middle/src/traits/structural_impls.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
3030
super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),
3131

3232
super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d),
33+
34+
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
3335
}
3436
}
3537
}
@@ -70,6 +72,16 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceBuiltinData<N> {
7072
}
7173
}
7274

75+
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<'tcx, N> {
76+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77+
write!(
78+
f,
79+
"ImplSourceTraitUpcastingData(upcast={:?}, vtable_vptr_slot={:?}, nested={:?})",
80+
self.upcast_trait_ref, self.vtable_vptr_slot, self.nested
81+
)
82+
}
83+
}
84+
7385
impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceAutoImplData<N> {
7486
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7587
write!(

‎compiler/rustc_mir/src/interpret/cast.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,16 +275,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
275275
return self.write_immediate(*val, dest);
276276
}
277277
// trait upcasting coercion
278-
let principal_a = data_a.principal().expect(
279-
"unsize_into_ptr: missing principal trait for trait upcasting coercion",
280-
);
281-
let principal_b = data_b.principal().expect(
282-
"unsize_into_ptr: missing principal trait for trait upcasting coercion",
283-
);
284-
285278
let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
286-
principal_a.with_self_ty(*self.tcx, src_pointee_ty),
287-
principal_b.with_self_ty(*self.tcx, src_pointee_ty),
279+
src_pointee_ty,
280+
dest_pointee_ty,
288281
));
289282

290283
if let Some(entry_idx) = vptr_entry_idx {

‎compiler/rustc_query_impl/src/keys.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,16 @@ impl<'tcx> Key for Ty<'tcx> {
332332
}
333333
}
334334

335+
impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
336+
#[inline(always)]
337+
fn query_crate_is_local(&self) -> bool {
338+
true
339+
}
340+
fn default_span(&self, _: TyCtxt<'_>) -> Span {
341+
DUMMY_SP
342+
}
343+
}
344+
335345
impl<'tcx> Key for &'tcx ty::List<ty::Predicate<'tcx>> {
336346
#[inline(always)]
337347
fn query_crate_is_local(&self) -> bool {

‎compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
2828
use rustc_errors::ErrorReported;
2929
use rustc_hir as hir;
3030
use rustc_hir::def_id::DefId;
31+
use rustc_hir::lang_items::LangItem;
3132
use rustc_middle::ty::fold::TypeFoldable;
3233
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
3334
use rustc_middle::ty::{
@@ -759,48 +760,38 @@ fn vtable_trait_first_method_offset<'tcx>(
759760
pub fn vtable_trait_upcasting_coercion_new_vptr_slot(
760761
tcx: TyCtxt<'tcx>,
761762
key: (
762-
ty::PolyTraitRef<'tcx>, // trait owning vtable
763-
ty::PolyTraitRef<'tcx>, // super trait ref
763+
Ty<'tcx>, // trait object type whose trait owning vtable
764+
Ty<'tcx>, // trait object for supertrait
764765
),
765766
) -> Option<usize> {
766-
let (trait_owning_vtable, super_trait_ref) = key;
767-
let super_trait_did = super_trait_ref.def_id();
768-
// FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur
769-
// multiple times.
767+
let (source, target) = key;
768+
assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
769+
assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());
770770

771-
let vtable_segment_callback = {
772-
let mut vptr_offset = 0;
773-
move |segment| {
774-
match segment {
775-
VtblSegment::MetadataDSA => {
776-
vptr_offset += COMMON_VTABLE_ENTRIES.len();
777-
}
778-
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
779-
vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
780-
if trait_ref.def_id() == super_trait_did {
781-
if emit_vptr {
782-
return ControlFlow::Break(Some(vptr_offset));
783-
} else {
784-
return ControlFlow::Break(None);
785-
}
786-
}
771+
// this has been typecked-before, so diagnostics is not really needed.
772+
let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
787773

788-
if emit_vptr {
789-
vptr_offset += 1;
790-
}
791-
}
792-
}
793-
ControlFlow::Continue(())
794-
}
774+
let trait_ref = ty::TraitRef {
775+
def_id: unsize_trait_did,
776+
substs: tcx.mk_substs_trait(source, &[target.into()]),
795777
};
778+
let obligation = Obligation::new(
779+
ObligationCause::dummy(),
780+
ty::ParamEnv::reveal_all(),
781+
ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: hir::Constness::NotConst }),
782+
);
796783

797-
if let Some(vptr_offset) =
798-
prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
799-
{
800-
vptr_offset
801-
} else {
802-
bug!("Failed to find info for expected trait in vtable");
803-
}
784+
let implsrc = tcx.infer_ctxt().enter(|infcx| {
785+
let mut selcx = SelectionContext::new(&infcx);
786+
selcx.select(&obligation).unwrap()
787+
});
788+
789+
let implsrc_traitcasting = match implsrc {
790+
Some(ImplSource::TraitUpcasting(data)) => data,
791+
_ => bug!(),
792+
};
793+
794+
implsrc_traitcasting.vtable_vptr_slot
804795
}
805796

806797
pub fn provide(providers: &mut ty::query::Providers) {

‎compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
14831483
// why we special case object types.
14841484
false
14851485
}
1486-
super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => {
1486+
super::ImplSource::AutoImpl(..)
1487+
| super::ImplSource::Builtin(..)
1488+
| super::ImplSource::TraitUpcasting(_) => {
14871489
// These traits have no associated types.
14881490
selcx.tcx().sess.delay_span_bug(
14891491
obligation.cause.span,
@@ -1554,6 +1556,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
15541556
| super::ImplSource::AutoImpl(..)
15551557
| super::ImplSource::Param(..)
15561558
| super::ImplSource::Builtin(..)
1559+
| super::ImplSource::TraitUpcasting(_)
15571560
| super::ImplSource::TraitAlias(..) => {
15581561
// we don't create Select candidates with this kind of resolution
15591562
span_bug!(

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

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -690,46 +690,80 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
690690

691691
debug!(?source, ?target, "assemble_candidates_for_unsizing");
692692

693-
let may_apply = match (source.kind(), target.kind()) {
693+
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-
// See `confirm_builtin_unsize_candidate` for more info.
696+
// Upcast coercions permit several 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+
// 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar`
701+
//
702+
// Note that neither of the first two of these changes requires any
703+
// change at runtime. The third needs to change pointer metadata at runtime.
704+
//
705+
// We always perform upcasting coercions when we can because of reason
706+
// #2 (region bounds).
697707
let auto_traits_compatible = data_b
698708
.auto_traits()
699709
// All of a's auto traits need to be in b's auto traits.
700710
.all(|b| data_a.auto_traits().any(|a| a == b));
701-
auto_traits_compatible
711+
if auto_traits_compatible {
712+
let principal_def_id_a = data_a.principal_def_id();
713+
let principal_def_id_b = data_b.principal_def_id();
714+
if principal_def_id_a == principal_def_id_b {
715+
// no cyclic
716+
candidates.vec.push(BuiltinUnsizeCandidate);
717+
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
718+
// not casual unsizing, now check whether this is trait upcasting coercion.
719+
let principal_a = data_a.principal().unwrap();
720+
let target_trait_did = principal_def_id_b.unwrap();
721+
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
722+
for (idx, upcast_trait_ref) in
723+
util::supertraits(self.tcx(), source_trait_ref).enumerate()
724+
{
725+
if upcast_trait_ref.def_id() == target_trait_did {
726+
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
727+
}
728+
}
729+
}
730+
}
702731
}
703732

704733
// `T` -> `Trait`
705-
(_, &ty::Dynamic(..)) => true,
734+
(_, &ty::Dynamic(..)) => {
735+
candidates.vec.push(BuiltinUnsizeCandidate);
736+
}
706737

707738
// Ambiguous handling is below `T` -> `Trait`, because inference
708739
// variables can still implement `Unsize<Trait>` and nested
709740
// obligations will have the final say (likely deferred).
710741
(&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => {
711742
debug!("assemble_candidates_for_unsizing: ambiguous");
712743
candidates.ambiguous = true;
713-
false
714744
}
715745

716746
// `[T; n]` -> `[T]`
717-
(&ty::Array(..), &ty::Slice(_)) => true,
747+
(&ty::Array(..), &ty::Slice(_)) => {
748+
candidates.vec.push(BuiltinUnsizeCandidate);
749+
}
718750

719751
// `Struct<T>` -> `Struct<U>`
720752
(&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => {
721-
def_id_a == def_id_b
753+
if def_id_a == def_id_b {
754+
candidates.vec.push(BuiltinUnsizeCandidate);
755+
}
722756
}
723757

724758
// `(.., T)` -> `(.., U)`
725-
(&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(),
759+
(&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => {
760+
if tys_a.len() == tys_b.len() {
761+
candidates.vec.push(BuiltinUnsizeCandidate);
762+
}
763+
}
726764

727-
_ => false,
765+
_ => {}
728766
};
729-
730-
if may_apply {
731-
candidates.vec.push(BuiltinUnsizeCandidate);
732-
}
733767
}
734768

735769
fn assemble_candidates_for_trait_alias(

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

Lines changed: 121 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ use crate::traits::Normalized;
2626
use crate::traits::OutputTypeParameterMismatch;
2727
use crate::traits::Selection;
2828
use crate::traits::TraitNotObjectSafe;
29+
use crate::traits::VtblSegment;
2930
use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation};
3031
use crate::traits::{
3132
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
3233
ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData,
3334
ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData,
34-
ImplSourceUserDefinedData,
35+
ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
3536
};
3637
use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation};
3738
use crate::traits::{Obligation, ObligationCause};
@@ -42,6 +43,7 @@ use super::SelectionCandidate::{self, *};
4243
use super::SelectionContext;
4344

4445
use std::iter;
46+
use std::ops::ControlFlow;
4547

4648
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
4749
#[instrument(level = "debug", skip(self))]
@@ -118,6 +120,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
118120
let data = self.confirm_builtin_unsize_candidate(obligation)?;
119121
Ok(ImplSource::Builtin(data))
120122
}
123+
124+
TraitUpcastingUnsizeCandidate(idx) => {
125+
let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
126+
Ok(ImplSource::TraitUpcasting(data))
127+
}
121128
}
122129
}
123130

@@ -685,10 +692,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
685692
.map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
686693
}
687694

688-
fn confirm_builtin_unsize_candidate(
695+
fn confirm_trait_upcasting_unsize_candidate(
689696
&mut self,
690697
obligation: &TraitObligation<'tcx>,
691-
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
698+
idx: usize,
699+
) -> Result<ImplSourceTraitUpcastingData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
700+
{
692701
let tcx = self.tcx();
693702

694703
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -697,62 +706,123 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
697706
let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
698707
let target = self.infcx.shallow_resolve(target);
699708

700-
debug!(?source, ?target, "confirm_builtin_unsize_candidate");
709+
debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate");
701710

702711
let mut nested = vec![];
712+
let source_trait_ref;
713+
let upcast_trait_ref;
703714
match (source.kind(), target.kind()) {
704-
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
715+
// TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion).
705716
(&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
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-
717+
// See `assemble_candidates_for_unsizing` for more info.
718718
// We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`.
719+
let principal_a = data_a.principal().unwrap();
720+
source_trait_ref = principal_a.with_self_ty(tcx, source);
721+
upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap();
722+
assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id()));
723+
let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| {
724+
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
725+
tcx, trait_ref,
726+
))
727+
});
728+
let iter = Some(existential_predicate)
729+
.into_iter()
730+
.chain(
731+
data_a
732+
.projection_bounds()
733+
.map(|b| b.map_bound(ty::ExistentialPredicate::Projection)),
734+
)
735+
.chain(
736+
data_b
737+
.auto_traits()
738+
.map(ty::ExistentialPredicate::AutoTrait)
739+
.map(ty::Binder::dummy),
740+
);
741+
let existential_predicates = tcx.mk_poly_existential_predicates(iter);
742+
let source_trait = tcx.mk_dynamic(existential_predicates, r_b);
719743

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);
744+
// Require that the traits involved in this upcast are **equal**;
745+
// only the **lifetime bound** is changed.
746+
let InferOk { obligations, .. } = self
747+
.infcx
748+
.at(&obligation.cause, obligation.param_env)
749+
.sup(target, source_trait)
750+
.map_err(|_| Unimplemented)?;
751+
nested.extend(obligations);
752+
753+
// Register one obligation for 'a: 'b.
754+
let cause = ObligationCause::new(
755+
obligation.cause.span,
756+
obligation.cause.body_id,
757+
ObjectCastObligation(target),
758+
);
759+
let outlives = ty::OutlivesPredicate(r_a, r_b);
760+
nested.push(Obligation::with_depth(
761+
cause,
762+
obligation.recursion_depth + 1,
763+
obligation.param_env,
764+
obligation.predicate.rebind(outlives).to_predicate(tcx),
765+
));
766+
}
767+
_ => bug!(),
768+
};
769+
770+
let vtable_segment_callback = {
771+
let mut vptr_offset = 0;
772+
move |segment| {
773+
match segment {
774+
VtblSegment::MetadataDSA => {
775+
vptr_offset += ty::COMMON_VTABLE_ENTRIES.len();
740776
}
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-
};
777+
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
778+
vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
779+
if trait_ref == upcast_trait_ref {
780+
if emit_vptr {
781+
return ControlFlow::Break(Some(vptr_offset));
782+
} else {
783+
return ControlFlow::Break(None);
784+
}
785+
}
786+
787+
if emit_vptr {
788+
vptr_offset += 1;
789+
}
790+
}
791+
}
792+
ControlFlow::Continue(())
793+
}
794+
};
795+
796+
let vtable_vptr_slot =
797+
super::super::prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback)
798+
.unwrap();
754799

755-
let iter = existential_predicate
800+
Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested })
801+
}
802+
803+
fn confirm_builtin_unsize_candidate(
804+
&mut self,
805+
obligation: &TraitObligation<'tcx>,
806+
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
807+
let tcx = self.tcx();
808+
809+
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
810+
// regions here. See the comment there for more details.
811+
let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
812+
let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
813+
let target = self.infcx.shallow_resolve(target);
814+
815+
debug!(?source, ?target, "confirm_builtin_unsize_candidate");
816+
817+
let mut nested = vec![];
818+
match (source.kind(), target.kind()) {
819+
// Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping).
820+
(&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
821+
// See `assemble_candidates_for_unsizing` for more info.
822+
// We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`.
823+
let iter = data_a
824+
.principal()
825+
.map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
756826
.into_iter()
757827
.chain(
758828
data_a

‎compiler/rustc_trait_selection/src/traits/select/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15161516
| FnPointerCandidate
15171517
| BuiltinObjectCandidate
15181518
| BuiltinUnsizeCandidate
1519+
| TraitUpcastingUnsizeCandidate(_)
15191520
| BuiltinCandidate { .. }
15201521
| TraitAliasCandidate(..)
15211522
| ObjectCandidate(_)
@@ -1533,6 +1534,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15331534
| FnPointerCandidate
15341535
| BuiltinObjectCandidate
15351536
| BuiltinUnsizeCandidate
1537+
| TraitUpcastingUnsizeCandidate(_)
15361538
| BuiltinCandidate { has_nested: true }
15371539
| TraitAliasCandidate(..),
15381540
ParamCandidate(ref cand),
@@ -1562,6 +1564,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15621564
| FnPointerCandidate
15631565
| BuiltinObjectCandidate
15641566
| BuiltinUnsizeCandidate
1567+
| TraitUpcastingUnsizeCandidate(_)
15651568
| BuiltinCandidate { .. }
15661569
| TraitAliasCandidate(..),
15671570
) => true,
@@ -1573,6 +1576,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15731576
| FnPointerCandidate
15741577
| BuiltinObjectCandidate
15751578
| BuiltinUnsizeCandidate
1579+
| TraitUpcastingUnsizeCandidate(_)
15761580
| BuiltinCandidate { .. }
15771581
| TraitAliasCandidate(..),
15781582
ObjectCandidate(_) | ProjectionCandidate(_),
@@ -1646,6 +1650,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16461650
| FnPointerCandidate
16471651
| BuiltinObjectCandidate
16481652
| BuiltinUnsizeCandidate
1653+
| TraitUpcastingUnsizeCandidate(_)
16491654
| BuiltinCandidate { has_nested: true }
16501655
| TraitAliasCandidate(..),
16511656
ImplCandidate(_)
@@ -1654,6 +1659,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16541659
| FnPointerCandidate
16551660
| BuiltinObjectCandidate
16561661
| BuiltinUnsizeCandidate
1662+
| TraitUpcastingUnsizeCandidate(_)
16571663
| BuiltinCandidate { has_nested: true }
16581664
| TraitAliasCandidate(..),
16591665
) => false,

‎compiler/rustc_ty_utils/src/instance.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,8 @@ fn resolve_associated_item<'tcx>(
381381
| traits::ImplSource::Param(..)
382382
| traits::ImplSource::TraitAlias(..)
383383
| traits::ImplSource::DiscriminantKind(..)
384-
| traits::ImplSource::Pointee(..) => None,
384+
| traits::ImplSource::Pointee(..)
385+
| traits::ImplSource::TraitUpcasting(_) => None,
385386
})
386387
}
387388

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// run-pass
2+
#![feature(trait_upcasting)]
3+
#![allow(incomplete_features)]
4+
5+
trait Foo<T: Default + ToString>: Bar<i32> + Bar<T> {}
6+
trait Bar<T: Default + ToString> {
7+
fn bar(&self) -> String {
8+
T::default().to_string()
9+
}
10+
}
11+
12+
struct S1;
13+
14+
impl Bar<i32> for S1 {}
15+
impl Foo<i32> for S1 {}
16+
17+
struct S2;
18+
impl Bar<i32> for S2 {}
19+
impl Bar<bool> for S2 {}
20+
impl Foo<bool> for S2 {}
21+
22+
fn test1(x: &dyn Foo<i32>) {
23+
let s = x as &dyn Bar<i32>;
24+
assert_eq!("0", &s.bar().to_string());
25+
}
26+
27+
fn test2(x: &dyn Foo<bool>) {
28+
let p = x as &dyn Bar<i32>;
29+
assert_eq!("0", &p.bar().to_string());
30+
let q = x as &dyn Bar<bool>;
31+
assert_eq!("false", &q.bar().to_string());
32+
}
33+
34+
fn main() {
35+
let s1 = S1;
36+
test1(&s1);
37+
let s2 = S2;
38+
test2(&s2);
39+
}

‎src/test/ui/traits/trait-upcasting/type-checking-test-1.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@ trait Bar<T> {
99
}
1010

1111
fn test_specific(x: &dyn Foo) {
12-
let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
13-
//~^ ERROR non-primitive cast
14-
//~^^ ERROR the trait bound `&dyn Foo: Bar<i32>` is not satisfied
15-
let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually
16-
//~^ ERROR non-primitive cast
17-
//~^^ ERROR the trait bound `&dyn Foo: Bar<u32>` is not satisfied
12+
let _ = x as &dyn Bar<i32>; // OK
13+
let _ = x as &dyn Bar<u32>; // OK
1814
}
1915

2016
fn test_unknown_version(x: &dyn Foo) {
@@ -24,9 +20,7 @@ fn test_unknown_version(x: &dyn Foo) {
2420
}
2521

2622
fn test_infer_version(x: &dyn Foo) {
27-
let a = x as &dyn Bar<_>; // FIXME: OK, eventually
28-
//~^ ERROR non-primitive cast
29-
//~^^ ERROR the trait bound `&dyn Foo: Bar<u32>` is not satisfied
23+
let a = x as &dyn Bar<_>; // OK
3024
let _: Option<u32> = a.bar();
3125
}
3226

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,5 @@
1-
error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<i32>`
2-
--> $DIR/type-checking-test-1.rs:12:13
3-
|
4-
LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
5-
| ^^^^^^^^^^^^^^^^^^ invalid cast
6-
|
7-
help: consider borrowing the value
8-
|
9-
LL | let _ = &x as &dyn Bar<i32>; // FIXME: OK, eventually
10-
| +
11-
12-
error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<u32>`
13-
--> $DIR/type-checking-test-1.rs:15:13
14-
|
15-
LL | let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually
16-
| ^^^^^^^^^^^^^^^^^^ invalid cast
17-
|
18-
help: consider borrowing the value
19-
|
20-
LL | let _ = &x as &dyn Bar<u32>; // FIXME: OK, eventually
21-
| +
22-
23-
error[E0277]: the trait bound `&dyn Foo: Bar<i32>` is not satisfied
24-
--> $DIR/type-checking-test-1.rs:12:13
25-
|
26-
LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
27-
| ^ the trait `Bar<i32>` is not implemented for `&dyn Foo`
28-
|
29-
= note: required for the cast to the object type `dyn Bar<i32>`
30-
31-
error[E0277]: the trait bound `&dyn Foo: Bar<u32>` is not satisfied
32-
--> $DIR/type-checking-test-1.rs:15:13
33-
|
34-
LL | let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually
35-
| ^ the trait `Bar<u32>` is not implemented for `&dyn Foo`
36-
|
37-
= note: required for the cast to the object type `dyn Bar<u32>`
38-
391
error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>`
40-
--> $DIR/type-checking-test-1.rs:21:13
2+
--> $DIR/type-checking-test-1.rs:17:13
413
|
424
LL | let _ = x as &dyn Bar<_>; // Ambiguous
435
| ^^^^^^^^^^^^^^^^ invalid cast
@@ -48,33 +10,14 @@ LL | let _ = &x as &dyn Bar<_>; // Ambiguous
4810
| +
4911

5012
error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied
51-
--> $DIR/type-checking-test-1.rs:21:13
13+
--> $DIR/type-checking-test-1.rs:17:13
5214
|
5315
LL | let _ = x as &dyn Bar<_>; // Ambiguous
5416
| ^ the trait `Bar<_>` is not implemented for `&dyn Foo`
5517
|
5618
= note: required for the cast to the object type `dyn Bar<_>`
5719

58-
error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<u32>`
59-
--> $DIR/type-checking-test-1.rs:27:13
60-
|
61-
LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually
62-
| ^^^^^^^^^^^^^^^^ invalid cast
63-
|
64-
help: consider borrowing the value
65-
|
66-
LL | let a = &x as &dyn Bar<_>; // FIXME: OK, eventually
67-
| +
68-
69-
error[E0277]: the trait bound `&dyn Foo: Bar<u32>` is not satisfied
70-
--> $DIR/type-checking-test-1.rs:27:13
71-
|
72-
LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually
73-
| ^ the trait `Bar<u32>` is not implemented for `&dyn Foo`
74-
|
75-
= note: required for the cast to the object type `dyn Bar<u32>`
76-
77-
error: aborting due to 8 previous errors
20+
error: aborting due to 2 previous errors
7821

7922
Some errors have detailed explanations: E0277, E0605.
8023
For more information about an error, try `rustc --explain E0277`.

‎src/test/ui/traits/trait-upcasting/type-checking-test-2.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ fn test_specific(x: &dyn Foo<i32>) {
1313
}
1414

1515
fn test_specific2(x: &dyn Foo<u32>) {
16-
let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
17-
//~^ ERROR non-primitive cast
18-
//~^^ ERROR the trait bound `&dyn Foo<u32>: Bar<i32>` is not satisfied
16+
let _ = x as &dyn Bar<i32>; // OK
1917
}
2018

2119
fn test_specific3(x: &dyn Foo<i32>) {
Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,5 @@
1-
error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<i32>`
2-
--> $DIR/type-checking-test-2.rs:16:13
3-
|
4-
LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
5-
| ^^^^^^^^^^^^^^^^^^ invalid cast
6-
|
7-
help: consider borrowing the value
8-
|
9-
LL | let _ = &x as &dyn Bar<i32>; // FIXME: OK, eventually
10-
| +
11-
12-
error[E0277]: the trait bound `&dyn Foo<u32>: Bar<i32>` is not satisfied
13-
--> $DIR/type-checking-test-2.rs:16:13
14-
|
15-
LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
16-
| ^ the trait `Bar<i32>` is not implemented for `&dyn Foo<u32>`
17-
|
18-
= note: required for the cast to the object type `dyn Bar<i32>`
19-
201
error[E0605]: non-primitive cast: `&dyn Foo<i32>` as `&dyn Bar<u32>`
21-
--> $DIR/type-checking-test-2.rs:22:13
2+
--> $DIR/type-checking-test-2.rs:20:13
223
|
234
LL | let _ = x as &dyn Bar<u32>; // Error
245
| ^^^^^^^^^^^^^^^^^^ invalid cast
@@ -29,15 +10,15 @@ LL | let _ = &x as &dyn Bar<u32>; // Error
2910
| +
3011

3112
error[E0277]: the trait bound `&dyn Foo<i32>: Bar<u32>` is not satisfied
32-
--> $DIR/type-checking-test-2.rs:22:13
13+
--> $DIR/type-checking-test-2.rs:20:13
3314
|
3415
LL | let _ = x as &dyn Bar<u32>; // Error
3516
| ^ the trait `Bar<u32>` is not implemented for `&dyn Foo<i32>`
3617
|
3718
= note: required for the cast to the object type `dyn Bar<u32>`
3819

3920
error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<_>`
40-
--> $DIR/type-checking-test-2.rs:28:13
21+
--> $DIR/type-checking-test-2.rs:26:13
4122
|
4223
LL | let a = x as &dyn Bar<_>; // Ambiguous
4324
| ^^^^^^^^^^^^^^^^ invalid cast
@@ -48,14 +29,14 @@ LL | let a = &x as &dyn Bar<_>; // Ambiguous
4829
| +
4930

5031
error[E0277]: the trait bound `&dyn Foo<u32>: Bar<_>` is not satisfied
51-
--> $DIR/type-checking-test-2.rs:28:13
32+
--> $DIR/type-checking-test-2.rs:26:13
5233
|
5334
LL | let a = x as &dyn Bar<_>; // Ambiguous
5435
| ^ the trait `Bar<_>` is not implemented for `&dyn Foo<u32>`
5536
|
5637
= note: required for the cast to the object type `dyn Bar<_>`
5738

58-
error: aborting due to 6 previous errors
39+
error: aborting due to 4 previous errors
5940

6041
Some errors have detailed explanations: E0277, E0605.
6142
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)
Please sign in to comment.