Skip to content

Commit

Permalink
Auto merge of #88135 - crlf0710:trait_upcasting_part_3, r=nikomatsakis
Browse files Browse the repository at this point in the history
Trait upcasting coercion (part 3)

By using separate candidates for each possible choice, this fixes type-checking issues in previous commits.

r? `@nikomatsakis`
  • Loading branch information
bors committed Aug 21, 2021
2 parents b1928aa + c22dfab commit d3e2578
Show file tree
Hide file tree
Showing 19 changed files with 324 additions and 239 deletions.
17 changes: 3 additions & 14 deletions compiler/rustc_codegen_cranelift/src/unsize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,10 @@ pub(crate) fn unsized_info<'tcx>(
if data_a.principal_def_id() == data_b.principal_def_id() {
return old_info;
}
// trait upcasting coercion

// if both of the two `principal`s are `None`, this function would have returned early above.
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
let principal_a = data_a
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");
let principal_b = data_b
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");

let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(fx.tcx, source),
principal_b.with_self_ty(fx.tcx, source),
));
// trait upcasting coercion
let vptr_entry_idx =
fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target));

if let Some(entry_idx) = vptr_entry_idx {
let entry_idx = u32::try_from(entry_idx).unwrap();
Expand Down
15 changes: 2 additions & 13 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,8 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

// trait upcasting coercion

// if both of the two `principal`s are `None`, this function would have returned early above.
// and if one of the two `principal`s is `None`, typechecking would have rejected this case.
let principal_a = data_a
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");
let principal_b = data_b
.principal()
.expect("unsized_info: missing principal trait for trait upcasting coercion");

let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(cx.tcx(), source),
principal_b.with_self_ty(cx.tcx(), source),
));
let vptr_entry_idx =
cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target));

if let Some(entry_idx) = vptr_entry_idx {
let ptr_ty = cx.type_i8p();
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,9 +987,9 @@ rustc_queries! {
desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) }
}

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

query codegen_fulfill_obligation(
Expand Down
31 changes: 29 additions & 2 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,9 @@ pub enum ImplSource<'tcx, N> {
/// Successful resolution for a builtin trait.
Builtin(ImplSourceBuiltinData<N>),

/// ImplSource for trait upcasting coercion
TraitUpcasting(ImplSourceTraitUpcastingData<'tcx, N>),

/// ImplSource automatically generated for a closure. The `DefId` is the ID
/// of the closure expression. This is a `ImplSource::UserDefined` in spirit, but the
/// impl is generated by the compiler and does not appear in the source.
Expand Down Expand Up @@ -540,6 +543,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
ImplSource::TraitAlias(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
}
}

Expand All @@ -556,6 +560,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
| ImplSource::Pointee(ImplSourcePointeeData) => &[],
ImplSource::TraitAlias(d) => &d.nested[..],
ImplSource::TraitUpcasting(d) => &d.nested[..],
}
}

Expand Down Expand Up @@ -607,6 +612,13 @@ impl<'tcx, N> ImplSource<'tcx, N> {
substs: d.substs,
nested: d.nested.into_iter().map(f).collect(),
}),
ImplSource::TraitUpcasting(d) => {
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData {
upcast_trait_ref: d.upcast_trait_ref,
vtable_vptr_slot: d.vtable_vptr_slot,
nested: d.nested.into_iter().map(f).collect(),
})
}
}
}
}
Expand Down Expand Up @@ -652,6 +664,20 @@ pub struct ImplSourceAutoImplData<N> {
pub nested: Vec<N>,
}

#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
pub struct ImplSourceTraitUpcastingData<'tcx, N> {
/// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,

/// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will
/// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable
/// within that vtable.
pub vtable_vptr_slot: Option<usize>,

pub nested: Vec<N>,
}

#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
pub struct ImplSourceBuiltinData<N> {
pub nested: Vec<N>,
Expand All @@ -663,8 +689,9 @@ pub struct ImplSourceObjectData<'tcx, N> {
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,

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

pub nested: Vec<N>,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ pub enum SelectionCandidate<'tcx> {
/// `rustc_infer::traits::util::supertraits`.
ObjectCandidate(usize),

/// Perform trait upcasting coercion of `dyn Trait` to a supertrait of `Trait`.
/// The index is the position in the iterator returned by
/// `rustc_infer::traits::util::supertraits`.
TraitUpcastingUnsizeCandidate(usize),

BuiltinObjectCandidate,

BuiltinUnsizeCandidate,
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/traits/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),

super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d),

super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
}
}
}
Expand Down Expand Up @@ -70,6 +72,16 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceBuiltinData<N> {
}
}

impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ImplSourceTraitUpcastingData(upcast={:?}, vtable_vptr_slot={:?}, nested={:?})",
self.upcast_trait_ref, self.vtable_vptr_slot, self.nested
)
}
}

impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceAutoImplData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
Expand Down
11 changes: 2 additions & 9 deletions compiler/rustc_mir/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,16 +275,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return self.write_immediate(*val, dest);
}
// trait upcasting coercion
let principal_a = data_a.principal().expect(
"unsize_into_ptr: missing principal trait for trait upcasting coercion",
);
let principal_b = data_b.principal().expect(
"unsize_into_ptr: missing principal trait for trait upcasting coercion",
);

let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(*self.tcx, src_pointee_ty),
principal_b.with_self_ty(*self.tcx, src_pointee_ty),
src_pointee_ty,
dest_pointee_ty,
));

if let Some(entry_idx) = vptr_entry_idx {
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_query_impl/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ impl<'tcx> Key for Ty<'tcx> {
}
}

impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
fn default_span(&self, _: TyCtxt<'_>) -> Span {
DUMMY_SP
}
}

impl<'tcx> Key for &'tcx ty::List<ty::Predicate<'tcx>> {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
Expand Down
63 changes: 27 additions & 36 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
use rustc_middle::ty::{
Expand Down Expand Up @@ -759,48 +760,38 @@ fn vtable_trait_first_method_offset<'tcx>(
pub fn vtable_trait_upcasting_coercion_new_vptr_slot(
tcx: TyCtxt<'tcx>,
key: (
ty::PolyTraitRef<'tcx>, // trait owning vtable
ty::PolyTraitRef<'tcx>, // super trait ref
Ty<'tcx>, // trait object type whose trait owning vtable
Ty<'tcx>, // trait object for supertrait
),
) -> Option<usize> {
let (trait_owning_vtable, super_trait_ref) = key;
let super_trait_did = super_trait_ref.def_id();
// FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur
// multiple times.
let (source, target) = key;
assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());

let vtable_segment_callback = {
let mut vptr_offset = 0;
move |segment| {
match segment {
VtblSegment::MetadataDSA => {
vptr_offset += COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
if trait_ref.def_id() == super_trait_did {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
return ControlFlow::Break(None);
}
}
// this has been typecked-before, so diagnostics is not really needed.
let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);

if emit_vptr {
vptr_offset += 1;
}
}
}
ControlFlow::Continue(())
}
let trait_ref = ty::TraitRef {
def_id: unsize_trait_did,
substs: tcx.mk_substs_trait(source, &[target.into()]),
};
let obligation = Obligation::new(
ObligationCause::dummy(),
ty::ParamEnv::reveal_all(),
ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: hir::Constness::NotConst }),
);

if let Some(vptr_offset) =
prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
{
vptr_offset
} else {
bug!("Failed to find info for expected trait in vtable");
}
let implsrc = tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
selcx.select(&obligation).unwrap()
});

let implsrc_traitcasting = match implsrc {
Some(ImplSource::TraitUpcasting(data)) => data,
_ => bug!(),
};

implsrc_traitcasting.vtable_vptr_slot
}

pub fn provide(providers: &mut ty::query::Providers) {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1483,7 +1483,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// why we special case object types.
false
}
super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => {
super::ImplSource::AutoImpl(..)
| super::ImplSource::Builtin(..)
| super::ImplSource::TraitUpcasting(_) => {
// These traits have no associated types.
selcx.tcx().sess.delay_span_bug(
obligation.cause.span,
Expand Down Expand Up @@ -1554,6 +1556,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
| super::ImplSource::AutoImpl(..)
| super::ImplSource::Param(..)
| super::ImplSource::Builtin(..)
| super::ImplSource::TraitUpcasting(_)
| super::ImplSource::TraitAlias(..) => {
// we don't create Select candidates with this kind of resolution
span_bug!(
Expand Down
Loading

0 comments on commit d3e2578

Please sign in to comment.