1
Fork 0

Do not treat vtable supertraits as distinct when bound with different bound vars

This commit is contained in:
Michael Goulet 2025-01-10 04:36:11 +00:00
parent 37a430e6ea
commit fdc4bd22b7
18 changed files with 146 additions and 115 deletions

View file

@ -245,7 +245,10 @@ pub(crate) fn data_id_for_vtable<'tcx>(
ty: Ty<'tcx>, ty: Ty<'tcx>,
trait_ref: Option<Binder<'tcx, ExistentialTraitRef<'tcx>>>, trait_ref: Option<Binder<'tcx, ExistentialTraitRef<'tcx>>>,
) -> DataId { ) -> DataId {
let alloc_id = tcx.vtable_allocation((ty, trait_ref)); let alloc_id = tcx.vtable_allocation((
ty,
trait_ref.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
));
data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
} }

View file

@ -234,7 +234,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
GlobalAlloc::VTable(ty, dyn_ty) => { GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc = self let alloc = self
.tcx .tcx
.global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) .global_alloc(self.tcx.vtable_allocation((
ty,
dyn_ty.principal().map(|principal| {
self.tcx.instantiate_bound_regions_with_erased(principal)
}),
)))
.unwrap_memory(); .unwrap_memory();
let init = const_alloc_to_gcc(self, alloc); let init = const_alloc_to_gcc(self, alloc);
self.static_addr_of(init, alloc.inner().align, None) self.static_addr_of(init, alloc.inner().align, None)

View file

@ -312,7 +312,12 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
GlobalAlloc::VTable(ty, dyn_ty) => { GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc = self let alloc = self
.tcx .tcx
.global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) .global_alloc(self.tcx.vtable_allocation((
ty,
dyn_ty.principal().map(|principal| {
self.tcx.instantiate_bound_regions_with_erased(principal)
}),
)))
.unwrap_memory(); .unwrap_memory();
let init = const_alloc_to_llvm(self, alloc, /*static*/ false); let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
let value = self.static_addr_of(init, alloc.inner().align, None); let value = self.static_addr_of(init, alloc.inner().align, None);

View file

@ -1406,7 +1406,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); let trait_ref = poly_trait_ref.with_self_ty(tcx, ty);
let trait_ref = tcx.erase_regions(trait_ref); let trait_ref = tcx.erase_regions(tcx.instantiate_bound_regions_with_erased(trait_ref));
tcx.vtable_entries(trait_ref) tcx.vtable_entries(trait_ref)
} else { } else {

View file

@ -96,24 +96,28 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option<ty::PolyExistentialTraitRef<'_>> {
pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
cx: &Cx, cx: &Cx,
ty: Ty<'tcx>, ty: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> Cx::Value { ) -> Cx::Value {
let tcx = cx.tcx(); let tcx = cx.tcx();
// Check the cache. // Check the cache.
if let Some(&val) = cx.vtables().borrow().get(&(ty, trait_ref)) { if let Some(&val) = cx.vtables().borrow().get(&(ty, poly_trait_ref)) {
return val; return val;
} }
// FIXME(trait_upcasting): Take a non-higher-ranked vtable as arg.
let trait_ref =
poly_trait_ref.map(|trait_ref| tcx.instantiate_bound_regions_with_erased(trait_ref));
let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref));
let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory();
let vtable_const = cx.const_data_from_alloc(vtable_allocation); let vtable_const = cx.const_data_from_alloc(vtable_allocation);
let align = cx.data_layout().pointer_align.abi; let align = cx.data_layout().pointer_align.abi;
let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); cx.apply_vcall_visibility_metadata(ty, poly_trait_ref, vtable);
cx.create_vtable_debuginfo(ty, trait_ref, vtable); cx.create_vtable_debuginfo(ty, poly_trait_ref, vtable);
cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); cx.vtables().borrow_mut().insert((ty, poly_trait_ref), vtable);
vtable vtable
} }

View file

@ -419,8 +419,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty)); self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
let vtable_entries = self.vtable_entries(data_a.principal(), ty); let vtable_entries = self.vtable_entries(data_a.principal(), ty);
if let Some(entry_idx) = vptr_entry_idx { if let Some(entry_idx) = vptr_entry_idx {
let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) = let Some(&ty::VtblEntry::TraitVPtr(_)) = vtable_entries.get(entry_idx)
vtable_entries.get(entry_idx)
else { else {
span_bug!( span_bug!(
self.cur_span(), self.cur_span(),
@ -429,13 +428,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
dest_pointee_ty dest_pointee_ty
); );
}; };
let erased_trait_ref = upcast_trait_ref
.map_bound(|r| ty::ExistentialTraitRef::erase_self_ty(*self.tcx, r));
assert!(
data_b
.principal()
.is_some_and(|b| self.eq_in_param_env(erased_trait_ref, b))
);
} else { } else {
// In this case codegen would keep using the old vtable. We don't want to do // In this case codegen would keep using the old vtable. We don't want to do
// that as it has the wrong trait. The reason codegen can do this is that // that as it has the wrong trait. The reason codegen can do this is that

View file

@ -54,7 +54,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> &'tcx [VtblEntry<'tcx>] { ) -> &'tcx [VtblEntry<'tcx>] {
if let Some(trait_) = trait_ { if let Some(trait_) = trait_ {
let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty); let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty);
let trait_ref = self.tcx.erase_regions(trait_ref); let trait_ref =
self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
self.tcx.vtable_entries(trait_ref) self.tcx.vtable_entries(trait_ref)
} else { } else {
TyCtxt::COMMON_VTABLE_ENTRIES TyCtxt::COMMON_VTABLE_ENTRIES

View file

@ -123,7 +123,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
); );
continue; continue;
}; };
tcx.vtable_entries(ty::Binder::dummy(trait_ref)) tcx.vtable_entries(trait_ref)
} }
hir::ItemKind::TyAlias(_, _) => { hir::ItemKind::TyAlias(_, _) => {
let ty = tcx.type_of(def_id).instantiate_identity(); let ty = tcx.type_of(def_id).instantiate_identity();
@ -149,7 +149,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
}; };
if let Some(principal) = data.principal() { if let Some(principal) = data.principal() {
tcx.vtable_entries( tcx.vtable_entries(
principal.map_bound(|principal| principal.with_self_ty(tcx, ty)), tcx.instantiate_bound_regions_with_erased(principal).with_self_ty(tcx, ty),
) )
} else { } else {
TyCtxt::COMMON_VTABLE_ENTRIES TyCtxt::COMMON_VTABLE_ENTRIES

View file

@ -95,7 +95,7 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
} }
} }
impl<'tcx> Key for (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) { impl<'tcx> Key for (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>) {
type Cache<V> = DefaultCache<Self, V>; type Cache<V> = DefaultCache<Self, V>;
fn default_span(&self, _: TyCtxt<'_>) -> Span { fn default_span(&self, _: TyCtxt<'_>) -> Span {

View file

@ -1437,9 +1437,9 @@ rustc_queries! {
desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) } desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) }
} }
query vtable_entries(key: ty::PolyTraitRef<'tcx>) query vtable_entries(key: ty::TraitRef<'tcx>)
-> &'tcx [ty::VtblEntry<'tcx>] { -> &'tcx [ty::VtblEntry<'tcx>] {
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 first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize { query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize {
@ -1451,7 +1451,7 @@ rustc_queries! {
key.1, key.0 } key.1, key.0 }
} }
query vtable_allocation(key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)) -> mir::interpret::AllocId { query vtable_allocation(key: (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>)) -> mir::interpret::AllocId {
desc { |tcx| "vtable const allocation for <{} as {}>", desc { |tcx| "vtable const allocation for <{} as {}>",
key.0, key.0,
key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned()) key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned())

View file

@ -855,7 +855,12 @@ where
} }
let mk_dyn_vtable = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { let mk_dyn_vtable = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
let min_count = ty::vtable_min_entries(tcx, principal); let min_count = ty::vtable_min_entries(
tcx,
principal.map(|principal| {
tcx.instantiate_bound_regions_with_erased(principal)
}),
);
Ty::new_imm_ref( Ty::new_imm_ref(
tcx, tcx,
tcx.lifetimes.re_static, tcx.lifetimes.re_static,

View file

@ -7,7 +7,7 @@ use rustc_type_ir::elaborate;
use crate::mir::interpret::{ use crate::mir::interpret::{
AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range, AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range,
}; };
use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt};
#[derive(Clone, Copy, PartialEq, HashStable)] #[derive(Clone, Copy, PartialEq, HashStable)]
pub enum VtblEntry<'tcx> { pub enum VtblEntry<'tcx> {
@ -22,7 +22,7 @@ pub enum VtblEntry<'tcx> {
/// dispatchable associated function /// dispatchable associated function
Method(Instance<'tcx>), Method(Instance<'tcx>),
/// pointer to a separate supertrait vtable, can be used by trait upcasting coercion /// pointer to a separate supertrait vtable, can be used by trait upcasting coercion
TraitVPtr(PolyTraitRef<'tcx>), TraitVPtr(TraitRef<'tcx>),
} }
impl<'tcx> fmt::Debug for VtblEntry<'tcx> { impl<'tcx> fmt::Debug for VtblEntry<'tcx> {
@ -59,7 +59,7 @@ pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
// function is an accurate approximation. We verify this when actually computing the vtable below. // function is an accurate approximation. We verify this when actually computing the vtable below.
pub(crate) fn vtable_min_entries<'tcx>( pub(crate) fn vtable_min_entries<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
) -> usize { ) -> usize {
let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len(); let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len();
let Some(trait_ref) = trait_ref else { let Some(trait_ref) = trait_ref else {
@ -67,7 +67,7 @@ pub(crate) fn vtable_min_entries<'tcx>(
}; };
// This includes self in supertraits. // This includes self in supertraits.
for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id()) { for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id) {
count += tcx.own_existential_vtable_entries(def_id).len(); count += tcx.own_existential_vtable_entries(def_id).len();
} }
@ -83,7 +83,7 @@ pub(crate) fn vtable_min_entries<'tcx>(
/// initial contents.) /// initial contents.)
pub(super) fn vtable_allocation_provider<'tcx>( pub(super) fn vtable_allocation_provider<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), key: (Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>),
) -> AllocId { ) -> AllocId {
let (ty, poly_trait_ref) = key; let (ty, poly_trait_ref) = key;
@ -118,7 +118,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
for (idx, entry) in vtable_entries.iter().enumerate() { for (idx, entry) in vtable_entries.iter().enumerate() {
let idx: u64 = u64::try_from(idx).unwrap(); let idx: u64 = u64::try_from(idx).unwrap();
let scalar = match entry { let scalar = match *entry {
VtblEntry::MetadataDropInPlace => { VtblEntry::MetadataDropInPlace => {
if ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) { if ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) {
let instance = ty::Instance::resolve_drop_in_place(tcx, ty); let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
@ -134,13 +134,12 @@ pub(super) fn vtable_allocation_provider<'tcx>(
VtblEntry::Vacant => continue, VtblEntry::Vacant => continue,
VtblEntry::Method(instance) => { VtblEntry::Method(instance) => {
// Prepare the fn ptr we write into the vtable. // Prepare the fn ptr we write into the vtable.
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(*instance, CTFE_ALLOC_SALT); let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT);
let fn_ptr = Pointer::from(fn_alloc_id); let fn_ptr = Pointer::from(fn_alloc_id);
Scalar::from_pointer(fn_ptr, &tcx) Scalar::from_pointer(fn_ptr, &tcx)
} }
VtblEntry::TraitVPtr(trait_ref) => { VtblEntry::TraitVPtr(trait_ref) => {
let super_trait_ref = trait_ref let super_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref))); let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref)));
let vptr = Pointer::from(supertrait_alloc_id); let vptr = Pointer::from(supertrait_alloc_id);
Scalar::from_pointer(vptr, &tcx) Scalar::from_pointer(vptr, &tcx)

View file

@ -1138,11 +1138,12 @@ fn create_mono_items_for_vtable_methods<'tcx>(
bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type"); bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type");
}; };
if let Some(principal) = trait_ty.principal() { if let Some(principal) = trait_ty.principal() {
let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); let trait_ref =
assert!(!poly_trait_ref.has_escaping_bound_vars()); tcx.instantiate_bound_regions_with_erased(principal.with_self_ty(tcx, impl_ty));
assert!(!trait_ref.has_escaping_bound_vars());
// Walk all methods of the trait, including those of its supertraits // Walk all methods of the trait, including those of its supertraits
let entries = tcx.vtable_entries(poly_trait_ref); let entries = tcx.vtable_entries(trait_ref);
debug!(?entries); debug!(?entries);
let methods = entries let methods = entries
.iter() .iter()
@ -1197,7 +1198,12 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt
} }
} }
GlobalAlloc::VTable(ty, dyn_ty) => { GlobalAlloc::VTable(ty, dyn_ty) => {
let alloc_id = tcx.vtable_allocation((ty, dyn_ty.principal())); let alloc_id = tcx.vtable_allocation((
ty,
dyn_ty
.principal()
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
));
collect_alloc(tcx, alloc_id, output) collect_alloc(tcx, alloc_id, output)
} }
} }

View file

@ -746,7 +746,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
let tcx = tables.tcx; let tcx = tables.tcx;
let alloc_id = tables.tcx.vtable_allocation(( let alloc_id = tables.tcx.vtable_allocation((
ty.internal(&mut *tables, tcx), ty.internal(&mut *tables, tcx),
trait_ref.internal(&mut *tables, tcx), trait_ref
.internal(&mut *tables, tcx)
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
)); ));
Some(alloc_id.stable(&mut *tables)) Some(alloc_id.stable(&mut *tables))
} }

View file

@ -2,8 +2,8 @@ use std::fmt::Debug;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::util::PredicateSet; use rustc_infer::traits::util::PredicateSet;
use rustc_middle::bug; use rustc_middle::bug;
@ -20,7 +20,7 @@ use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum VtblSegment<'tcx> { pub enum VtblSegment<'tcx> {
MetadataDSA, MetadataDSA,
TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool }, TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool },
} }
/// Prepare the segments for a vtable /// Prepare the segments for a vtable
@ -28,7 +28,7 @@ pub enum VtblSegment<'tcx> {
// about our `Self` type here. // about our `Self` type here.
pub fn prepare_vtable_segments<'tcx, T>( pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
) -> Option<T> { ) -> Option<T> {
prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
@ -38,7 +38,7 @@ pub fn prepare_vtable_segments<'tcx, T>(
/// such that we can use `?` in the body. /// such that we can use `?` in the body.
fn prepare_vtable_segments_inner<'tcx, T>( fn prepare_vtable_segments_inner<'tcx, T>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
) -> ControlFlow<T> { ) -> ControlFlow<T> {
// The following constraints holds for the final arrangement. // The following constraints holds for the final arrangement.
@ -91,7 +91,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
let mut emit_vptr_on_new_entry = false; let mut emit_vptr_on_new_entry = false;
let mut visited = PredicateSet::new(tcx); let mut visited = PredicateSet::new(tcx);
let predicate = trait_ref.upcast(tcx); let predicate = trait_ref.upcast(tcx);
let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> =
smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
visited.insert(predicate); visited.insert(predicate);
@ -124,10 +124,19 @@ fn prepare_vtable_segments_inner<'tcx, T>(
let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
let mut direct_super_traits_iter = tcx let mut direct_super_traits_iter = tcx
.explicit_super_predicates_of(inner_most_trait_ref.def_id()) .explicit_super_predicates_of(inner_most_trait_ref.def_id)
.iter_identity_copied() .iter_identity_copied()
.filter_map(move |(pred, _)| { .filter_map(move |(pred, _)| {
pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause() Some(
tcx.instantiate_bound_regions_with_erased(
pred.instantiate_supertrait(
tcx,
ty::Binder::dummy(inner_most_trait_ref),
)
.as_trait_clause()?,
)
.trait_ref,
)
}); });
// Find an unvisited supertrait // Find an unvisited supertrait
@ -135,16 +144,11 @@ fn prepare_vtable_segments_inner<'tcx, T>(
.find(|&super_trait| visited.insert(super_trait.upcast(tcx))) .find(|&super_trait| visited.insert(super_trait.upcast(tcx)))
{ {
// Push it to the stack for the next iteration of 'diving_in to pick up // Push it to the stack for the next iteration of 'diving_in to pick up
Some(unvisited_super_trait) => { Some(next_super_trait) => stack.push((
// We're throwing away potential constness of super traits here. next_super_trait,
// FIXME: handle ~const super traits emit_vptr_on_new_entry,
let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); maybe_iter(Some(direct_super_traits_iter)),
stack.push(( )),
next_super_trait,
emit_vptr_on_new_entry,
maybe_iter(Some(direct_super_traits_iter)),
))
}
// There are no more unvisited direct super traits, dive-in finished // There are no more unvisited direct super traits, dive-in finished
None => break 'diving_in, None => break 'diving_in,
@ -153,8 +157,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
// emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
let has_entries = let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id);
has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id());
segment_visitor(VtblSegment::TraitOwnEntries { segment_visitor(VtblSegment::TraitOwnEntries {
trait_ref: inner_most_trait_ref, trait_ref: inner_most_trait_ref,
@ -168,11 +171,6 @@ fn prepare_vtable_segments_inner<'tcx, T>(
if let Some(next_inner_most_trait_ref) = if let Some(next_inner_most_trait_ref) =
siblings.find(|&sibling| visited.insert(sibling.upcast(tcx))) siblings.find(|&sibling| visited.insert(sibling.upcast(tcx)))
{ {
// We're throwing away potential constness of super traits here.
// FIXME: handle ~const super traits
let next_inner_most_trait_ref =
next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
// just pushed a new trait onto the stack, so we need to go through its super traits // just pushed a new trait onto the stack, so we need to go through its super traits
@ -229,7 +227,7 @@ fn own_existential_vtable_entries_iter(
/// that come from `trait_ref`, including its supertraits. /// that come from `trait_ref`, including its supertraits.
fn vtable_entries<'tcx>( fn vtable_entries<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
) -> &'tcx [VtblEntry<'tcx>] { ) -> &'tcx [VtblEntry<'tcx>] {
debug!("vtable_entries({:?})", trait_ref); debug!("vtable_entries({:?})", trait_ref);
@ -241,33 +239,26 @@ fn vtable_entries<'tcx>(
entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES); entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
} }
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
let existential_trait_ref = trait_ref let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
// Lookup the shape of vtable for the trait. // Lookup the shape of vtable for the trait.
let own_existential_entries = let own_existential_entries =
tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
let own_entries = own_existential_entries.iter().copied().map(|def_id| { let own_entries = own_existential_entries.iter().copied().map(|def_id| {
debug!("vtable_entries: trait_method={:?}", def_id); debug!("vtable_entries: trait_method={:?}", def_id);
// The method may have some early-bound lifetimes; add regions for those. // The method may have some early-bound lifetimes; add regions for those.
let args = trait_ref.map_bound(|trait_ref| { // FIXME: Is this normalize needed?
let args = tcx.normalize_erasing_regions(
ty::TypingEnv::fully_monomorphized(),
GenericArgs::for_item(tcx, def_id, |param, _| match param.kind { GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
GenericParamDefKind::Type { .. } GenericParamDefKind::Type { .. }
| GenericParamDefKind::Const { .. } => { | GenericParamDefKind::Const { .. } => {
trait_ref.args[param.index as usize] trait_ref.args[param.index as usize]
} }
}) }),
});
// The trait type may have higher-ranked lifetimes in it;
// erase them if they appear, so that we get the type
// at some particular call site.
let args = tcx.normalize_erasing_late_bound_regions(
ty::TypingEnv::fully_monomorphized(),
args,
); );
// It's possible that the method relies on where-clauses that // It's possible that the method relies on where-clauses that
@ -318,10 +309,11 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
bug!(); bug!();
}; };
let source_principal = let source_principal = tcx.instantiate_bound_regions_with_erased(
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self),
);
let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key)); let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
let vtable_segment_callback = { let vtable_segment_callback = {
let mut vptr_offset = 0; let mut vptr_offset = 0;
@ -333,15 +325,14 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
if trait_refs_are_compatible( if trait_refs_are_compatible(
tcx, tcx,
vtable_principal ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
target_principal, target_principal,
) { ) {
return ControlFlow::Break(vptr_offset); return ControlFlow::Break(vptr_offset);
} }
vptr_offset += vptr_offset +=
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
if emit_vptr { if emit_vptr {
vptr_offset += 1; vptr_offset += 1;
@ -373,14 +364,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
let ty::Dynamic(target, _, _) = *target.kind() else { let ty::Dynamic(target, _, _) = *target.kind() else {
bug!(); bug!();
}; };
let target_principal = target.principal()?; let target_principal = tcx.instantiate_bound_regions_with_erased(target.principal()?);
// Given that we have a target principal, it is a bug for there not to be a source principal. // Given that we have a target principal, it is a bug for there not to be a source principal.
let ty::Dynamic(source, _, _) = *source.kind() else { let ty::Dynamic(source, _, _) = *source.kind() else {
bug!(); bug!();
}; };
let source_principal = let source_principal = tcx.instantiate_bound_regions_with_erased(
source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self),
);
let vtable_segment_callback = { let vtable_segment_callback = {
let mut vptr_offset = 0; let mut vptr_offset = 0;
@ -391,11 +383,10 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
} }
VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
vptr_offset += vptr_offset +=
tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
if trait_refs_are_compatible( if trait_refs_are_compatible(
tcx, tcx,
vtable_principal ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
.map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
target_principal, target_principal,
) { ) {
if emit_vptr { if emit_vptr {
@ -419,37 +410,32 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
fn trait_refs_are_compatible<'tcx>( fn trait_refs_are_compatible<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>, vtable_principal: ty::ExistentialTraitRef<'tcx>,
hr_target_principal: ty::PolyExistentialTraitRef<'tcx>, target_principal: ty::ExistentialTraitRef<'tcx>,
) -> bool { ) -> bool {
if hr_vtable_principal.def_id() != hr_target_principal.def_id() { if vtable_principal.def_id != target_principal.def_id {
return false; return false;
} }
let (infcx, param_env) = let (infcx, param_env) =
tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized()); tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
let ocx = ObligationCtxt::new(&infcx); let ocx = ObligationCtxt::new(&infcx);
let hr_source_principal = let source_principal = ocx.normalize(&ObligationCause::dummy(), param_env, vtable_principal);
ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal); let target_principal = ocx.normalize(&ObligationCause::dummy(), param_env, target_principal);
let hr_target_principal = let Ok(()) = ocx.eq_trace(
ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal); &ObligationCause::dummy(),
infcx.enter_forall(hr_target_principal, |target_principal| { param_env,
let source_principal = infcx.instantiate_binder_with_fresh_vars( ToTrace::to_trace(
DUMMY_SP,
BoundRegionConversionTime::HigherRankedType,
hr_source_principal,
);
let Ok(()) = ocx.eq_trace(
&ObligationCause::dummy(), &ObligationCause::dummy(),
param_env, ty::Binder::dummy(target_principal),
ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal), ty::Binder::dummy(source_principal),
target_principal, ),
source_principal, target_principal,
) else { source_principal,
return false; ) else {
}; return false;
ocx.select_all_or_error().is_empty() };
}) ocx.select_all_or_error().is_empty()
} }
pub(super) fn provide(providers: &mut Providers) { pub(super) fn provide(providers: &mut Providers) {

View file

@ -3,8 +3,6 @@ error: vtable entries: [
MetadataSize, MetadataSize,
MetadataAlign, MetadataAlign,
Method(<dyn for<'a> Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), Method(<dyn for<'a> Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
Method(<dyn for<'a> Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
TraitVPtr(for<'a> <dyn for<'a> Trait<&(), &'a ()> as Supertrait<&'a ()>>),
Method(<dyn for<'a> Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)), Method(<dyn for<'a> Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)),
] ]
--> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1 --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1

View file

@ -0,0 +1,24 @@
//@ run-pass
//@ check-run-results
#![feature(trait_upcasting)]
trait Supertrait<T> {
fn _print_numbers(&self, mem: &[usize; 100]) {
println!("{mem:?}");
}
}
impl<T> Supertrait<T> for () {}
trait Trait<T, U>: Supertrait<T> + Supertrait<U> {
fn say_hello(&self, _: &usize) {
println!("Hello!");
}
}
impl<T, U> Trait<T, U> for () {}
fn main() {
(&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
as &'static dyn Trait<&'static (), &'static ()>)
.say_hello(&0);
}