Skip to content

Commit d3e2578

Browse files
committed
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`
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

+3-14
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

+2-13
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

+3-3
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

+29-2
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

+5
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

+12
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

+2-9
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

+10
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

+27-36
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

+4-1
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!(

0 commit comments

Comments
 (0)