Only compute vtable information during codegen
This commit is contained in:
parent
f8e5660532
commit
3b9adbec32
14 changed files with 141 additions and 195 deletions
|
@ -39,8 +39,7 @@ pub(crate) fn unsized_info<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// trait upcasting coercion
|
// trait upcasting coercion
|
||||||
let vptr_entry_idx =
|
let vptr_entry_idx = fx.tcx.supertrait_vtable_slot((source, target));
|
||||||
fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
|
@ -163,8 +163,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
|
|
||||||
// trait upcasting coercion
|
// trait upcasting coercion
|
||||||
|
|
||||||
let vptr_entry_idx =
|
let vptr_entry_idx = cx.tcx().supertrait_vtable_slot((source, target));
|
||||||
cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
|
|
||||||
|
|
||||||
if let Some(entry_idx) = vptr_entry_idx {
|
if let Some(entry_idx) = vptr_entry_idx {
|
||||||
let ptr_size = bx.data_layout().pointer_size;
|
let ptr_size = bx.data_layout().pointer_size;
|
||||||
|
|
|
@ -360,6 +360,14 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Key for ty::TraitRef<'tcx> {
|
||||||
|
type Cache<V> = DefaultCache<Self, V>;
|
||||||
|
|
||||||
|
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
|
||||||
|
tcx.def_span(self.def_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
|
impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
|
||||||
type Cache<V> = DefaultCache<Self, V>;
|
type Cache<V> = DefaultCache<Self, V>;
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ use crate::traits::{
|
||||||
};
|
};
|
||||||
use crate::ty::fast_reject::SimplifiedType;
|
use crate::ty::fast_reject::SimplifiedType;
|
||||||
use crate::ty::layout::ValidityRequirement;
|
use crate::ty::layout::ValidityRequirement;
|
||||||
|
use crate::ty::print::PrintTraitRefExt;
|
||||||
use crate::ty::util::AlwaysRequiresDrop;
|
use crate::ty::util::AlwaysRequiresDrop;
|
||||||
use crate::ty::TyCtxtFeed;
|
use crate::ty::TyCtxtFeed;
|
||||||
use crate::ty::{
|
use crate::ty::{
|
||||||
|
@ -1271,7 +1272,11 @@ 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<'tcx>, Ty<'tcx>)) -> Option<usize> {
|
query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize {
|
||||||
|
desc { |tcx| "finding the slot within the vtable of `{}` for the implementation of `{}`", key.self_ty(), key.print_only_trait_name() }
|
||||||
|
}
|
||||||
|
|
||||||
|
query supertrait_vtable_slot(key: (Ty<'tcx>, Ty<'tcx>)) -> Option<usize> {
|
||||||
desc { |tcx| "finding the slot within vtable for trait object `{}` 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",
|
||||||
key.1, key.0 }
|
key.1, key.0 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::InferCtxt;
|
use rustc_infer::infer::InferCtxt;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
|
use rustc_infer::traits::util::supertraits;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::traits::solve::inspect::ProbeKind;
|
use rustc_middle::traits::solve::inspect::ProbeKind;
|
||||||
use rustc_middle::traits::solve::{
|
use rustc_middle::traits::solve::{
|
||||||
|
@ -743,14 +744,14 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
|
||||||
// a projection goal.
|
// a projection goal.
|
||||||
if let Some(principal) = bounds.principal() {
|
if let Some(principal) = bounds.principal() {
|
||||||
let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
|
let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
|
||||||
self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| {
|
for (idx, assumption) in supertraits(self.interner(), principal_trait_ref).enumerate() {
|
||||||
candidates.extend(G::probe_and_consider_object_bound_candidate(
|
candidates.extend(G::probe_and_consider_object_bound_candidate(
|
||||||
ecx,
|
self,
|
||||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base }),
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)),
|
||||||
goal,
|
goal,
|
||||||
assumption.upcast(tcx),
|
assumption.upcast(tcx),
|
||||||
));
|
));
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Gen
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use crate::traits::coherence;
|
use crate::traits::coherence;
|
||||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
|
||||||
|
|
||||||
use super::inspect::ProofTreeBuilder;
|
use super::inspect::ProofTreeBuilder;
|
||||||
use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT};
|
use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT};
|
||||||
|
@ -1022,41 +1021,6 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor`
|
|
||||||
/// for every trait ref encountered (including the principal). Passes both the vtable
|
|
||||||
/// base and the (optional) vptr slot.
|
|
||||||
pub(super) fn walk_vtable(
|
|
||||||
&mut self,
|
|
||||||
principal: ty::PolyTraitRef<'tcx>,
|
|
||||||
mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>),
|
|
||||||
) {
|
|
||||||
let tcx = self.interner();
|
|
||||||
let mut offset = 0;
|
|
||||||
prepare_vtable_segments::<()>(tcx, principal, |segment| {
|
|
||||||
match segment {
|
|
||||||
VtblSegment::MetadataDSA => {
|
|
||||||
offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
|
||||||
}
|
|
||||||
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
|
|
||||||
let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref);
|
|
||||||
|
|
||||||
supertrait_visitor(
|
|
||||||
self,
|
|
||||||
trait_ref,
|
|
||||||
offset,
|
|
||||||
emit_vptr.then(|| offset + own_vtable_entries),
|
|
||||||
);
|
|
||||||
|
|
||||||
offset += own_vtable_entries;
|
|
||||||
if emit_vptr {
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ControlFlow::Continue(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`
|
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`
|
||||||
|
|
|
@ -114,8 +114,8 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>(
|
||||||
// In the old trait solver, we arbitrarily choose lower vtable candidates
|
// In the old trait solver, we arbitrarily choose lower vtable candidates
|
||||||
// over higher ones.
|
// over higher ones.
|
||||||
(
|
(
|
||||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base: a }),
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)),
|
||||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base: b }),
|
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
|
||||||
) => a >= b,
|
) => a >= b,
|
||||||
// Prefer dyn candidates over non-dyn candidates. This is necessary to
|
// Prefer dyn candidates over non-dyn candidates. This is necessary to
|
||||||
// handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
|
// handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
|
||||||
|
|
|
@ -9,6 +9,7 @@ use rustc_hir::{LangItem, Movability};
|
||||||
use rustc_infer::infer::InferCtxt;
|
use rustc_infer::infer::InferCtxt;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::solve::MaybeCause;
|
use rustc_infer::traits::solve::MaybeCause;
|
||||||
|
use rustc_infer::traits::util::supertraits;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::traits::solve::inspect::ProbeKind;
|
use rustc_middle::traits::solve::inspect::ProbeKind;
|
||||||
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult};
|
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult};
|
||||||
|
@ -756,24 +757,19 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
|
||||||
a_data.principal(),
|
a_data.principal(),
|
||||||
));
|
));
|
||||||
} else if let Some(a_principal) = a_data.principal() {
|
} else if let Some(a_principal) = a_data.principal() {
|
||||||
self.walk_vtable(
|
for new_a_principal in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)).skip(1) {
|
||||||
a_principal.with_self_ty(tcx, a_ty),
|
responses.extend(self.consider_builtin_upcast_to_principal(
|
||||||
|ecx, new_a_principal, _, vtable_vptr_slot| {
|
goal,
|
||||||
responses.extend(ecx.consider_builtin_upcast_to_principal(
|
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
|
||||||
goal,
|
a_data,
|
||||||
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting {
|
a_region,
|
||||||
vtable_vptr_slot,
|
b_data,
|
||||||
}),
|
b_region,
|
||||||
a_data,
|
Some(new_a_principal.map_bound(|trait_ref| {
|
||||||
a_region,
|
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||||
b_data,
|
})),
|
||||||
b_region,
|
));
|
||||||
Some(new_a_principal.map_bound(|trait_ref| {
|
}
|
||||||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
|
||||||
})),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
responses
|
responses
|
||||||
|
|
|
@ -65,7 +65,7 @@ pub use self::structural_match::search_for_structural_match_violation;
|
||||||
pub use self::structural_normalize::StructurallyNormalizeExt;
|
pub use self::structural_normalize::StructurallyNormalizeExt;
|
||||||
pub use self::util::elaborate;
|
pub use self::util::elaborate;
|
||||||
pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
|
pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
|
||||||
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
|
pub use self::util::{impl_item_is_final, upcast_choices};
|
||||||
pub use self::util::{supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item};
|
pub use self::util::{supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item};
|
||||||
pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
|
pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,6 @@ use rustc_span::def_id::DefId;
|
||||||
|
|
||||||
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
|
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
|
||||||
use crate::traits::util::{self, closure_trait_ref_and_return_type};
|
use crate::traits::util::{self, closure_trait_ref_and_return_type};
|
||||||
use crate::traits::vtable::{
|
|
||||||
count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset,
|
|
||||||
VtblSegment,
|
|
||||||
};
|
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation,
|
ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation,
|
||||||
ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError,
|
ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError,
|
||||||
|
@ -689,13 +685,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
|
|
||||||
debug!(?nested, "object nested obligations");
|
debug!(?nested, "object nested obligations");
|
||||||
|
|
||||||
let vtable_base = vtable_trait_first_method_offset(
|
Ok(ImplSource::Builtin(BuiltinImplSource::Object(index), nested))
|
||||||
tcx,
|
|
||||||
unnormalized_upcast_trait_ref,
|
|
||||||
ty::Binder::dummy(object_trait_ref),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_fn_pointer_candidate(
|
fn confirm_fn_pointer_candidate(
|
||||||
|
@ -1125,36 +1115,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
)?
|
)?
|
||||||
.expect("did not expect ambiguity during confirmation");
|
.expect("did not expect ambiguity during confirmation");
|
||||||
|
|
||||||
let vtable_segment_callback = {
|
Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting, nested))
|
||||||
let mut vptr_offset = 0;
|
|
||||||
move |segment| {
|
|
||||||
match segment {
|
|
||||||
VtblSegment::MetadataDSA => {
|
|
||||||
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
|
||||||
}
|
|
||||||
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
|
|
||||||
vptr_offset += count_own_vtable_entries(tcx, trait_ref);
|
|
||||||
if trait_ref == unnormalized_upcast_principal {
|
|
||||||
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 =
|
|
||||||
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap();
|
|
||||||
|
|
||||||
Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_builtin_unsize_candidate(
|
fn confirm_builtin_unsize_candidate(
|
||||||
|
|
|
@ -208,23 +208,6 @@ pub fn upcast_choices<'tcx>(
|
||||||
supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
|
supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given an upcast trait object described by `object`, returns the
|
|
||||||
/// index of the method `method_def_id` (which should be part of
|
|
||||||
/// `object.upcast_trait_ref`) within the vtable for `object`.
|
|
||||||
pub fn get_vtable_index_of_object_method<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
vtable_base: usize,
|
|
||||||
method_def_id: DefId,
|
|
||||||
) -> Option<usize> {
|
|
||||||
// Count number of methods preceding the one we are selecting and
|
|
||||||
// add them to the total offset.
|
|
||||||
tcx.own_existential_vtable_entries(tcx.parent(method_def_id))
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.position(|def_id| def_id == method_def_id)
|
|
||||||
.map(|index| vtable_base + index)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn closure_trait_ref_and_return_type<'tcx>(
|
pub fn closure_trait_ref_and_return_type<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
fn_trait_def_id: DefId,
|
fn_trait_def_id: DefId,
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
use crate::errors::DumpVTableEntries;
|
use crate::errors::DumpVTableEntries;
|
||||||
use crate::traits::{impossible_predicates, is_vtable_safe_method};
|
use crate::traits::{impossible_predicates, is_vtable_safe_method};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::lang_items::LangItem;
|
|
||||||
use rustc_infer::traits::util::PredicateSet;
|
use rustc_infer::traits::util::PredicateSet;
|
||||||
use rustc_infer::traits::ImplSource;
|
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
use rustc_middle::traits::BuiltinImplSource;
|
|
||||||
use rustc_middle::ty::visit::TypeVisitableExt;
|
|
||||||
use rustc_middle::ty::GenericArgs;
|
|
||||||
use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry};
|
use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry};
|
||||||
|
use rustc_middle::ty::{GenericArgs, TypeVisitableExt};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
|
@ -320,30 +316,42 @@ fn vtable_entries<'tcx>(
|
||||||
tcx.arena.alloc_from_iter(entries)
|
tcx.arena.alloc_from_iter(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find slot base for trait methods within vtable entries of another trait
|
// Given a `dyn Subtrait: Supertrait` trait ref, find corresponding first slot
|
||||||
pub(super) fn vtable_trait_first_method_offset<'tcx>(
|
// for `Supertrait`'s methods in the vtable of `Subtrait`.
|
||||||
tcx: TyCtxt<'tcx>,
|
pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
|
||||||
trait_to_be_found: ty::PolyTraitRef<'tcx>,
|
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
|
||||||
trait_owning_vtable: ty::PolyTraitRef<'tcx>,
|
|
||||||
) -> usize {
|
let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
|
||||||
// #90177
|
bug!();
|
||||||
let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found);
|
};
|
||||||
|
let source_principal = tcx
|
||||||
|
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
|
||||||
|
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||||
|
|
||||||
|
let target_principal = tcx
|
||||||
|
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), key)
|
||||||
|
// We don't care about the self type, since it will always be the same thing.
|
||||||
|
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||||
|
|
||||||
let vtable_segment_callback = {
|
let vtable_segment_callback = {
|
||||||
let mut vtable_base = 0;
|
let mut vptr_offset = 0;
|
||||||
|
|
||||||
move |segment| {
|
move |segment| {
|
||||||
match segment {
|
match segment {
|
||||||
VtblSegment::MetadataDSA => {
|
VtblSegment::MetadataDSA => {
|
||||||
vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
||||||
}
|
}
|
||||||
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
|
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
|
||||||
if tcx.erase_regions(trait_ref) == trait_to_be_found_erased {
|
if tcx
|
||||||
return ControlFlow::Break(vtable_base);
|
.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref)
|
||||||
|
== target_principal
|
||||||
|
{
|
||||||
|
return ControlFlow::Break(vptr_offset);
|
||||||
}
|
}
|
||||||
vtable_base += count_own_vtable_entries(tcx, trait_ref);
|
|
||||||
|
vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len();
|
||||||
|
|
||||||
if emit_vptr {
|
if emit_vptr {
|
||||||
vtable_base += 1;
|
vptr_offset += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,55 +359,72 @@ pub(super) fn vtable_trait_first_method_offset<'tcx>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(vtable_base) =
|
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
|
||||||
prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
|
|
||||||
{
|
|
||||||
vtable_base
|
|
||||||
} else {
|
|
||||||
bug!("Failed to find info for expected trait in vtable");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find slot offset for trait vptr within vtable entries of another trait
|
/// Given a `dyn Subtrait` and `dyn Supertrait` trait object, find the slot of
|
||||||
pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
|
/// // the trait vptr in the subtrait's vtable.
|
||||||
|
pub(crate) fn supertrait_vtable_slot<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
key: (
|
key: (
|
||||||
Ty<'tcx>, // trait object type whose trait owning vtable
|
Ty<'tcx>, // Source -- `dyn Subtrait`.
|
||||||
Ty<'tcx>, // trait object for supertrait
|
Ty<'tcx>, // Target -- `dyn Supertrait` being coerced to.
|
||||||
),
|
),
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
|
debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
|
||||||
|
|
||||||
let (source, target) = key;
|
let (source, target) = key;
|
||||||
assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.has_infer());
|
let ty::Dynamic(source, _, _) = *source.kind() else {
|
||||||
assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.has_infer());
|
bug!();
|
||||||
|
};
|
||||||
|
let source_principal = tcx
|
||||||
|
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
|
||||||
|
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||||
|
|
||||||
// this has been typecked-before, so diagnostics is not really needed.
|
let ty::Dynamic(target, _, _) = *target.kind() else {
|
||||||
let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
|
bug!();
|
||||||
|
};
|
||||||
|
let target_principal = tcx
|
||||||
|
.normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal().unwrap())
|
||||||
|
.with_self_ty(tcx, tcx.types.trait_object_dummy_self);
|
||||||
|
|
||||||
let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
|
let vtable_segment_callback = {
|
||||||
|
let mut vptr_offset = 0;
|
||||||
|
move |segment| {
|
||||||
|
match segment {
|
||||||
|
VtblSegment::MetadataDSA => {
|
||||||
|
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
|
||||||
|
}
|
||||||
|
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
|
||||||
|
vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len();
|
||||||
|
if tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), trait_ref)
|
||||||
|
== target_principal
|
||||||
|
{
|
||||||
|
if emit_vptr {
|
||||||
|
return ControlFlow::Break(Some(vptr_offset));
|
||||||
|
} else {
|
||||||
|
return ControlFlow::Break(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
|
if emit_vptr {
|
||||||
Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => {
|
vptr_offset += 1;
|
||||||
*vtable_vptr_slot
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a trait `trait_ref`, returns the number of vtable entries
|
prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
|
||||||
/// that come from `trait_ref`, excluding its supertraits. Used in
|
|
||||||
/// computing the vtable base for an upcast trait of a trait object.
|
|
||||||
pub(crate) fn count_own_vtable_entries<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
|
||||||
) -> usize {
|
|
||||||
tcx.own_existential_vtable_entries(trait_ref.def_id()).len()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn provide(providers: &mut Providers) {
|
pub(super) fn provide(providers: &mut Providers) {
|
||||||
*providers = Providers {
|
*providers = Providers {
|
||||||
own_existential_vtable_entries,
|
own_existential_vtable_entries,
|
||||||
vtable_entries,
|
vtable_entries,
|
||||||
vtable_trait_upcasting_coercion_new_vptr_slot,
|
first_method_vtable_slot,
|
||||||
|
supertrait_vtable_slot,
|
||||||
..*providers
|
..*providers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,13 +212,23 @@ fn resolve_associated_item<'tcx>(
|
||||||
|
|
||||||
Some(ty::Instance::new(leaf_def.item.def_id, args))
|
Some(ty::Instance::new(leaf_def.item.def_id, args))
|
||||||
}
|
}
|
||||||
traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => {
|
traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => {
|
||||||
traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map(
|
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_args);
|
||||||
|index| Instance {
|
if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() {
|
||||||
def: ty::InstanceDef::Virtual(trait_item_id, index),
|
// We only resolve totally substituted vtable entries.
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let vtable_base = tcx.first_method_vtable_slot(trait_ref);
|
||||||
|
let offset = tcx
|
||||||
|
.own_existential_vtable_entries(trait_id)
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.position(|def_id| def_id == trait_item_id);
|
||||||
|
offset.map(|offset| Instance {
|
||||||
|
def: ty::InstanceDef::Virtual(trait_item_id, vtable_base + offset),
|
||||||
args: rcvr_args,
|
args: rcvr_args,
|
||||||
},
|
})
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
|
traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
|
||||||
let lang_items = tcx.lang_items();
|
let lang_items = tcx.lang_items();
|
||||||
|
|
|
@ -171,25 +171,20 @@ pub enum CandidateSource<I: Interner> {
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
|
||||||
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
|
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
|
||||||
pub enum BuiltinImplSource {
|
pub enum BuiltinImplSource {
|
||||||
/// Some builtin impl we don't need to differentiate. This should be used
|
/// Some built-in impl we don't need to differentiate. This should be used
|
||||||
/// unless more specific information is necessary.
|
/// unless more specific information is necessary.
|
||||||
Misc,
|
Misc,
|
||||||
/// A builtin impl for trait objects.
|
/// A built-in impl for trait objects. The index is only used in winnowing.
|
||||||
|
Object(usize),
|
||||||
|
/// A built-in implementation of `Upcast` for trait objects to other trait objects.
|
||||||
///
|
///
|
||||||
/// The vtable is formed by concatenating together the method lists of
|
/// This can be removed when `feature(dyn_upcasting)` is stabilized, since we only
|
||||||
/// the base object trait and all supertraits, pointers to supertrait vtable will
|
/// use it to detect when upcasting traits in hir typeck.
|
||||||
/// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
|
TraitUpcasting,
|
||||||
/// in that vtable.
|
|
||||||
Object { vtable_base: usize },
|
|
||||||
/// 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.
|
|
||||||
TraitUpcasting { vtable_vptr_slot: Option<usize> },
|
|
||||||
/// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
|
/// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`.
|
||||||
///
|
///
|
||||||
/// This needs to be a separate variant as it is still unstable and we need to emit
|
/// This can be removed when `feature(tuple_unsizing)` is stabilized, since we only
|
||||||
/// a feature error when using it on stable.
|
/// use it to detect when unsizing tuples in hir typeck.
|
||||||
TupleUnsizing,
|
TupleUnsizing,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue