1
Fork 0

Fold vtable_trait_upcasting_coercion_new_vptr_slot logic into obligation processing.

This commit is contained in:
Charles Lew 2021-08-18 12:45:18 +08:00
parent 1e605023ec
commit 6b1c52ff25
11 changed files with 137 additions and 87 deletions

View file

@ -31,21 +31,10 @@ pub(crate) fn unsized_info<'tcx>(
if data_a.principal_def_id() == data_b.principal_def_id() { if data_a.principal_def_id() == data_b.principal_def_id() {
return old_info; return old_info;
} }
// trait upcasting coercion // trait upcasting coercion
let vptr_entry_idx =
// if both of the two `principal`s are `None`, this function would have returned early above. fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
// 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),
));
if let Some(entry_idx) = vptr_entry_idx { if let Some(entry_idx) = vptr_entry_idx {
let entry_idx = u32::try_from(entry_idx).unwrap(); let entry_idx = u32::try_from(entry_idx).unwrap();

View file

@ -150,19 +150,8 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// trait upcasting coercion // trait upcasting coercion
// if both of the two `principal`s are `None`, this function would have returned early above. let vptr_entry_idx =
// and if one of the two `principal`s is `None`, typechecking would have rejected this case. cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
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),
));
if let Some(entry_idx) = vptr_entry_idx { if let Some(entry_idx) = vptr_entry_idx {
let ptr_ty = cx.type_i8p(); let ptr_ty = cx.type_i8p();

View file

@ -987,9 +987,9 @@ rustc_queries! {
desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) } 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> { 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 {} vtable ptr during trait upcasting coercion from {} vtable", desc { |tcx| "finding the slot within vtable for trait object {} vtable ptr during trait upcasting coercion from {} vtable",
tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) } key.1, key.0 }
} }
query codegen_fulfill_obligation( query codegen_fulfill_obligation(

View file

@ -503,6 +503,9 @@ pub enum ImplSource<'tcx, N> {
/// Successful resolution for a builtin trait. /// Successful resolution for a builtin trait.
Builtin(ImplSourceBuiltinData<N>), Builtin(ImplSourceBuiltinData<N>),
/// ImplSource for trait upcasting coercion
TraitUpcasting(ImplSourceTraitUpcastingData<'tcx, N>),
/// ImplSource automatically generated for a closure. The `DefId` is the ID /// ImplSource automatically generated for a closure. The `DefId` is the ID
/// of the closure expression. This is a `ImplSource::UserDefined` in spirit, but the /// 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. /// impl is generated by the compiler and does not appear in the source.
@ -538,6 +541,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
| ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
ImplSource::TraitAlias(d) => d.nested, ImplSource::TraitAlias(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
} }
} }
@ -554,6 +558,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
| ImplSource::Pointee(ImplSourcePointeeData) => &[], | ImplSource::Pointee(ImplSourcePointeeData) => &[],
ImplSource::TraitAlias(d) => &d.nested[..], ImplSource::TraitAlias(d) => &d.nested[..],
ImplSource::TraitUpcasting(d) => &d.nested[..],
} }
} }
@ -605,6 +610,13 @@ impl<'tcx, N> ImplSource<'tcx, N> {
substs: d.substs, substs: d.substs,
nested: d.nested.into_iter().map(f).collect(), 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(),
})
}
} }
} }
} }
@ -650,6 +662,20 @@ pub struct ImplSourceAutoImplData<N> {
pub nested: Vec<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)] #[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)]
pub struct ImplSourceBuiltinData<N> { pub struct ImplSourceBuiltinData<N> {
pub nested: Vec<N>, pub nested: Vec<N>,
@ -661,8 +687,9 @@ pub struct ImplSourceObjectData<'tcx, N> {
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
/// The vtable is formed by concatenating together the method lists of /// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits; this is the start of /// the base object trait and all supertraits, pointers to supertrait vtable will
/// `upcast_trait_ref`'s methods in that vtable. /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
/// in that vtable.
pub vtable_base: usize, pub vtable_base: usize,
pub nested: Vec<N>, pub nested: Vec<N>,

View file

@ -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::Builtin(ref d) => write!(f, "{:?}", d),
super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d), super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d),
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
} }
} }
} }
@ -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> { impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceAutoImplData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(

View file

@ -275,16 +275,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return self.write_immediate(*val, dest); return self.write_immediate(*val, dest);
} }
// trait upcasting coercion // 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(( let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
principal_a.with_self_ty(*self.tcx, src_pointee_ty), src_pointee_ty,
principal_b.with_self_ty(*self.tcx, src_pointee_ty), dest_pointee_ty,
)); ));
if let Some(entry_idx) = vptr_entry_idx { if let Some(entry_idx) = vptr_entry_idx {

View file

@ -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>> { impl<'tcx> Key for &'tcx ty::List<ty::Predicate<'tcx>> {
#[inline(always)] #[inline(always)]
fn query_crate_is_local(&self) -> bool { fn query_crate_is_local(&self) -> bool {

View file

@ -28,6 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_errors::ErrorReported; use rustc_errors::ErrorReported;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -759,48 +760,38 @@ fn vtable_trait_first_method_offset<'tcx>(
pub fn vtable_trait_upcasting_coercion_new_vptr_slot( pub fn vtable_trait_upcasting_coercion_new_vptr_slot(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
key: ( key: (
ty::PolyTraitRef<'tcx>, // trait owning vtable Ty<'tcx>, // trait object type whose trait owning vtable
ty::PolyTraitRef<'tcx>, // super trait ref Ty<'tcx>, // trait object for supertrait
), ),
) -> Option<usize> { ) -> Option<usize> {
let (trait_owning_vtable, super_trait_ref) = key; let (source, target) = key;
let super_trait_did = super_trait_ref.def_id(); debug_assert!(matches!(&source.kind(), &ty::Dynamic(..)));
// FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur debug_assert!(matches!(&target.kind(), &ty::Dynamic(..)));
// multiple times.
let vtable_segment_callback = { // this has been typecked-before, so diagnostics is not really needed.
let mut vptr_offset = 0; let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
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);
}
}
if emit_vptr { let trait_ref = ty::TraitRef {
vptr_offset += 1; def_id: unsize_trait_did,
} substs: tcx.mk_substs_trait(source, &[target.into()]),
} };
} let obligation = Obligation::new(
ControlFlow::Continue(()) ObligationCause::dummy(),
} ty::ParamEnv::reveal_all(),
ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: hir::Constness::NotConst }),
);
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!(),
}; };
if let Some(vptr_offset) = implsrc_traitcasting.vtable_vptr_slot
prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
{
vptr_offset
} else {
bug!("Failed to find info for expected trait in vtable");
}
} }
pub fn provide(providers: &mut ty::query::Providers) { pub fn provide(providers: &mut ty::query::Providers) {

View file

@ -1483,7 +1483,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// why we special case object types. // why we special case object types.
false false
} }
super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => { super::ImplSource::AutoImpl(..)
| super::ImplSource::Builtin(..)
| super::ImplSource::TraitUpcasting(_) => {
// These traits have no associated types. // These traits have no associated types.
selcx.tcx().sess.delay_span_bug( selcx.tcx().sess.delay_span_bug(
obligation.cause.span, obligation.cause.span,
@ -1554,6 +1556,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
| super::ImplSource::AutoImpl(..) | super::ImplSource::AutoImpl(..)
| super::ImplSource::Param(..) | super::ImplSource::Param(..)
| super::ImplSource::Builtin(..) | super::ImplSource::Builtin(..)
| super::ImplSource::TraitUpcasting(_)
| super::ImplSource::TraitAlias(..) => { | super::ImplSource::TraitAlias(..) => {
// we don't create Select candidates with this kind of resolution // we don't create Select candidates with this kind of resolution
span_bug!( span_bug!(

View file

@ -26,12 +26,13 @@ use crate::traits::Normalized;
use crate::traits::OutputTypeParameterMismatch; use crate::traits::OutputTypeParameterMismatch;
use crate::traits::Selection; use crate::traits::Selection;
use crate::traits::TraitNotObjectSafe; use crate::traits::TraitNotObjectSafe;
use crate::traits::VtblSegment;
use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation}; use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation};
use crate::traits::{ use crate::traits::{
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData,
ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData,
ImplSourceUserDefinedData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
}; };
use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation}; use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation};
use crate::traits::{Obligation, ObligationCause}; use crate::traits::{Obligation, ObligationCause};
@ -42,6 +43,7 @@ use super::SelectionCandidate::{self, *};
use super::SelectionContext; use super::SelectionContext;
use std::iter; use std::iter;
use std::ops::ControlFlow;
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
@ -121,7 +123,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
TraitUpcastingUnsizeCandidate(idx) => { TraitUpcastingUnsizeCandidate(idx) => {
let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?;
Ok(ImplSource::Builtin(data)) Ok(ImplSource::TraitUpcasting(data))
} }
} }
} }
@ -694,7 +696,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self, &mut self,
obligation: &TraitObligation<'tcx>, obligation: &TraitObligation<'tcx>,
idx: usize, idx: usize,
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { ) -> Result<ImplSourceTraitUpcastingData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
{
let tcx = self.tcx(); let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound // `assemble_candidates_for_unsizing` should ensure there are no late-bound
@ -706,16 +709,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate"); debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate");
let mut nested = vec![]; let mut nested = vec![];
let source_trait_ref;
let upcast_trait_ref;
match (source.kind(), target.kind()) { match (source.kind(), target.kind()) {
// TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion).
(&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
// See `assemble_candidates_for_unsizing` for more info. // See `assemble_candidates_for_unsizing` for more info.
// We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`.
let principal_a = data_a.principal().unwrap(); let principal_a = data_a.principal().unwrap();
let source_trait_ref = principal_a.with_self_ty(tcx, source); source_trait_ref = principal_a.with_self_ty(tcx, source);
let target_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap(); upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap();
assert_eq!(data_b.principal_def_id(), Some(target_trait_ref.def_id())); assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id()));
let existential_predicate = target_trait_ref.map_bound(|trait_ref| { let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| {
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
tcx, trait_ref, tcx, trait_ref,
)) ))
@ -762,7 +767,37 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => bug!(), _ => bug!(),
}; };
Ok(ImplSourceBuiltinData { nested }) let vtable_segment_callback = {
let mut vptr_offset = 0;
move |segment| {
match segment {
VtblSegment::MetadataDSA => {
vptr_offset += ty::COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
if trait_ref == upcast_trait_ref {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
} else {
return ControlFlow::Break(None);
}
}
if emit_vptr {
vptr_offset += 1;
}
}
}
ControlFlow::Continue(())
}
};
let vtable_vptr_slot =
super::super::prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback)
.unwrap();
Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested })
} }
fn confirm_builtin_unsize_candidate( fn confirm_builtin_unsize_candidate(

View file

@ -381,7 +381,8 @@ fn resolve_associated_item<'tcx>(
| traits::ImplSource::Param(..) | traits::ImplSource::Param(..)
| traits::ImplSource::TraitAlias(..) | traits::ImplSource::TraitAlias(..)
| traits::ImplSource::DiscriminantKind(..) | traits::ImplSource::DiscriminantKind(..)
| traits::ImplSource::Pointee(..) => None, | traits::ImplSource::Pointee(..)
| traits::ImplSource::TraitUpcasting(_) => None,
}) })
} }