Skip to content

Commit b36035c

Browse files
Move vtable methods into its own module
1 parent 4827004 commit b36035c

File tree

4 files changed

+398
-378
lines changed

4 files changed

+398
-378
lines changed

compiler/rustc_trait_selection/src/traits/mod.rs

+4-363
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,21 @@ mod select;
2020
mod specialize;
2121
mod structural_match;
2222
mod util;
23+
mod vtable;
2324
pub mod wf;
2425

25-
use crate::errors::DumpVTableEntries;
2626
use crate::infer::outlives::env::OutlivesEnvironment;
2727
use crate::infer::{InferCtxt, TyCtxtInferExt};
2828
use crate::traits::error_reporting::TypeErrCtxtExt as _;
2929
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
3030
use rustc_errors::ErrorGuaranteed;
3131
use rustc_hir as hir;
3232
use rustc_hir::def_id::DefId;
33-
use rustc_hir::lang_items::LangItem;
3433
use rustc_middle::ty::fold::TypeFoldable;
3534
use rustc_middle::ty::visit::TypeVisitable;
36-
use rustc_middle::ty::{
37-
self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, VtblEntry,
38-
};
35+
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
3936
use rustc_middle::ty::{InternalSubsts, SubstsRef};
40-
use rustc_span::{sym, Span};
41-
use smallvec::SmallVec;
37+
use rustc_span::Span;
4238

4339
use std::fmt::Debug;
4440
use std::ops::ControlFlow;
@@ -567,368 +563,13 @@ fn is_impossible_method<'tcx>(
567563
false
568564
}
569565

570-
#[derive(Clone, Debug)]
571-
enum VtblSegment<'tcx> {
572-
MetadataDSA,
573-
TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
574-
}
575-
576-
/// Prepare the segments for a vtable
577-
fn prepare_vtable_segments<'tcx, T>(
578-
tcx: TyCtxt<'tcx>,
579-
trait_ref: ty::PolyTraitRef<'tcx>,
580-
mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
581-
) -> Option<T> {
582-
// The following constraints holds for the final arrangement.
583-
// 1. The whole virtual table of the first direct super trait is included as the
584-
// the prefix. If this trait doesn't have any super traits, then this step
585-
// consists of the dsa metadata.
586-
// 2. Then comes the proper pointer metadata(vptr) and all own methods for all
587-
// other super traits except those already included as part of the first
588-
// direct super trait virtual table.
589-
// 3. finally, the own methods of this trait.
590-
591-
// This has the advantage that trait upcasting to the first direct super trait on each level
592-
// is zero cost, and to another trait includes only replacing the pointer with one level indirection,
593-
// while not using too much extra memory.
594-
595-
// For a single inheritance relationship like this,
596-
// D --> C --> B --> A
597-
// The resulting vtable will consists of these segments:
598-
// DSA, A, B, C, D
599-
600-
// For a multiple inheritance relationship like this,
601-
// D --> C --> A
602-
// \-> B
603-
// The resulting vtable will consists of these segments:
604-
// DSA, A, B, B-vptr, C, D
605-
606-
// For a diamond inheritance relationship like this,
607-
// D --> B --> A
608-
// \-> C -/
609-
// The resulting vtable will consists of these segments:
610-
// DSA, A, B, C, C-vptr, D
611-
612-
// For a more complex inheritance relationship like this:
613-
// O --> G --> C --> A
614-
// \ \ \-> B
615-
// | |-> F --> D
616-
// | \-> E
617-
// |-> N --> J --> H
618-
// \ \-> I
619-
// |-> M --> K
620-
// \-> L
621-
// The resulting vtable will consists of these segments:
622-
// DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G,
623-
// H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr,
624-
// N, N-vptr, O
625-
626-
// emit dsa segment first.
627-
if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) {
628-
return Some(v);
629-
}
630-
631-
let mut emit_vptr_on_new_entry = false;
632-
let mut visited = util::PredicateSet::new(tcx);
633-
let predicate = trait_ref.without_const().to_predicate(tcx);
634-
let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
635-
smallvec![(trait_ref, emit_vptr_on_new_entry, None)];
636-
visited.insert(predicate);
637-
638-
// the main traversal loop:
639-
// basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
640-
// that each node is emitted after all its descendents have been emitted.
641-
// so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
642-
// this is done on the fly.
643-
// Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
644-
// stops after it finds a node that has a next-sibling node.
645-
// This next-sibling node will used as the starting point of next slice.
646-
647-
// Example:
648-
// For a diamond inheritance relationship like this,
649-
// D#1 --> B#0 --> A#0
650-
// \-> C#1 -/
651-
652-
// Starting point 0 stack [D]
653-
// Loop run #0: Stack after diving in is [D B A], A is "childless"
654-
// after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one.
655-
// Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here.
656-
// Loop run #0: Stack after exiting out is [D C], C is the next starting point.
657-
// Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted).
658-
// Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
659-
// Loop run #1: Stack after exiting out is []. Now the function exits.
660-
661-
loop {
662-
// dive deeper into the stack, recording the path
663-
'diving_in: loop {
664-
if let Some((inner_most_trait_ref, _, _)) = stack.last() {
665-
let inner_most_trait_ref = *inner_most_trait_ref;
666-
let mut direct_super_traits_iter = tcx
667-
.super_predicates_of(inner_most_trait_ref.def_id())
668-
.predicates
669-
.into_iter()
670-
.filter_map(move |(pred, _)| {
671-
pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred()
672-
});
673-
674-
'diving_in_skip_visited_traits: loop {
675-
if let Some(next_super_trait) = direct_super_traits_iter.next() {
676-
if visited.insert(next_super_trait.to_predicate(tcx)) {
677-
// We're throwing away potential constness of super traits here.
678-
// FIXME: handle ~const super traits
679-
let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref);
680-
stack.push((
681-
next_super_trait,
682-
emit_vptr_on_new_entry,
683-
Some(direct_super_traits_iter),
684-
));
685-
break 'diving_in_skip_visited_traits;
686-
} else {
687-
continue 'diving_in_skip_visited_traits;
688-
}
689-
} else {
690-
break 'diving_in;
691-
}
692-
}
693-
}
694-
}
695-
696-
// Other than the left-most path, vptr should be emitted for each trait.
697-
emit_vptr_on_new_entry = true;
698-
699-
// emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
700-
'exiting_out: loop {
701-
if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() {
702-
if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries {
703-
trait_ref: *inner_most_trait_ref,
704-
emit_vptr: *emit_vptr,
705-
}) {
706-
return Some(v);
707-
}
708-
709-
'exiting_out_skip_visited_traits: loop {
710-
if let Some(siblings) = siblings_opt {
711-
if let Some(next_inner_most_trait_ref) = siblings.next() {
712-
if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) {
713-
// We're throwing away potential constness of super traits here.
714-
// FIXME: handle ~const super traits
715-
let next_inner_most_trait_ref =
716-
next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
717-
*inner_most_trait_ref = next_inner_most_trait_ref;
718-
*emit_vptr = emit_vptr_on_new_entry;
719-
break 'exiting_out;
720-
} else {
721-
continue 'exiting_out_skip_visited_traits;
722-
}
723-
}
724-
}
725-
stack.pop();
726-
continue 'exiting_out;
727-
}
728-
}
729-
// all done
730-
return None;
731-
}
732-
}
733-
}
734-
735-
fn dump_vtable_entries<'tcx>(
736-
tcx: TyCtxt<'tcx>,
737-
sp: Span,
738-
trait_ref: ty::PolyTraitRef<'tcx>,
739-
entries: &[VtblEntry<'tcx>],
740-
) {
741-
tcx.sess.emit_err(DumpVTableEntries {
742-
span: sp,
743-
trait_ref,
744-
entries: format!("{:#?}", entries),
745-
});
746-
}
747-
748-
fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] {
749-
let trait_methods = tcx
750-
.associated_items(trait_def_id)
751-
.in_definition_order()
752-
.filter(|item| item.kind == ty::AssocKind::Fn);
753-
// Now list each method's DefId (for within its trait).
754-
let own_entries = trait_methods.filter_map(move |trait_method| {
755-
debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
756-
let def_id = trait_method.def_id;
757-
758-
// Some methods cannot be called on an object; skip those.
759-
if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) {
760-
debug!("own_existential_vtable_entry: not vtable safe");
761-
return None;
762-
}
763-
764-
Some(def_id)
765-
});
766-
767-
tcx.arena.alloc_from_iter(own_entries.into_iter())
768-
}
769-
770-
/// Given a trait `trait_ref`, iterates the vtable entries
771-
/// that come from `trait_ref`, including its supertraits.
772-
fn vtable_entries<'tcx>(
773-
tcx: TyCtxt<'tcx>,
774-
trait_ref: ty::PolyTraitRef<'tcx>,
775-
) -> &'tcx [VtblEntry<'tcx>] {
776-
debug!("vtable_entries({:?})", trait_ref);
777-
778-
let mut entries = vec![];
779-
780-
let vtable_segment_callback = |segment| -> ControlFlow<()> {
781-
match segment {
782-
VtblSegment::MetadataDSA => {
783-
entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
784-
}
785-
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
786-
let existential_trait_ref = trait_ref
787-
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
788-
789-
// Lookup the shape of vtable for the trait.
790-
let own_existential_entries =
791-
tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
792-
793-
let own_entries = own_existential_entries.iter().copied().map(|def_id| {
794-
debug!("vtable_entries: trait_method={:?}", def_id);
795-
796-
// The method may have some early-bound lifetimes; add regions for those.
797-
let substs = trait_ref.map_bound(|trait_ref| {
798-
InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
799-
GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
800-
GenericParamDefKind::Type { .. }
801-
| GenericParamDefKind::Const { .. } => {
802-
trait_ref.substs[param.index as usize]
803-
}
804-
})
805-
});
806-
807-
// The trait type may have higher-ranked lifetimes in it;
808-
// erase them if they appear, so that we get the type
809-
// at some particular call site.
810-
let substs = tcx
811-
.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), substs);
812-
813-
// It's possible that the method relies on where-clauses that
814-
// do not hold for this particular set of type parameters.
815-
// Note that this method could then never be called, so we
816-
// do not want to try and codegen it, in that case (see #23435).
817-
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
818-
if impossible_predicates(tcx, predicates.predicates) {
819-
debug!("vtable_entries: predicates do not hold");
820-
return VtblEntry::Vacant;
821-
}
822-
823-
let instance = ty::Instance::resolve_for_vtable(
824-
tcx,
825-
ty::ParamEnv::reveal_all(),
826-
def_id,
827-
substs,
828-
)
829-
.expect("resolution failed during building vtable representation");
830-
VtblEntry::Method(instance)
831-
});
832-
833-
entries.extend(own_entries);
834-
835-
if emit_vptr {
836-
entries.push(VtblEntry::TraitVPtr(trait_ref));
837-
}
838-
}
839-
}
840-
841-
ControlFlow::Continue(())
842-
};
843-
844-
let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
845-
846-
if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
847-
let sp = tcx.def_span(trait_ref.def_id());
848-
dump_vtable_entries(tcx, sp, trait_ref, &entries);
849-
}
850-
851-
tcx.arena.alloc_from_iter(entries.into_iter())
852-
}
853-
854-
/// Find slot base for trait methods within vtable entries of another trait
855-
fn vtable_trait_first_method_offset<'tcx>(
856-
tcx: TyCtxt<'tcx>,
857-
key: (
858-
ty::PolyTraitRef<'tcx>, // trait_to_be_found
859-
ty::PolyTraitRef<'tcx>, // trait_owning_vtable
860-
),
861-
) -> usize {
862-
let (trait_to_be_found, trait_owning_vtable) = key;
863-
864-
// #90177
865-
let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found);
866-
867-
let vtable_segment_callback = {
868-
let mut vtable_base = 0;
869-
870-
move |segment| {
871-
match segment {
872-
VtblSegment::MetadataDSA => {
873-
vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len();
874-
}
875-
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
876-
if tcx.erase_regions(trait_ref) == trait_to_be_found_erased {
877-
return ControlFlow::Break(vtable_base);
878-
}
879-
vtable_base += util::count_own_vtable_entries(tcx, trait_ref);
880-
if emit_vptr {
881-
vtable_base += 1;
882-
}
883-
}
884-
}
885-
ControlFlow::Continue(())
886-
}
887-
};
888-
889-
if let Some(vtable_base) =
890-
prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
891-
{
892-
vtable_base
893-
} else {
894-
bug!("Failed to find info for expected trait in vtable");
895-
}
896-
}
897-
898-
/// Find slot offset for trait vptr within vtable entries of another trait
899-
pub fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
900-
tcx: TyCtxt<'tcx>,
901-
key: (
902-
Ty<'tcx>, // trait object type whose trait owning vtable
903-
Ty<'tcx>, // trait object for supertrait
904-
),
905-
) -> Option<usize> {
906-
let (source, target) = key;
907-
assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
908-
assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());
909-
910-
// this has been typecked-before, so diagnostics is not really needed.
911-
let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
912-
913-
let trait_ref = tcx.mk_trait_ref(unsize_trait_did, [source, target]);
914-
915-
match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) {
916-
Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
917-
implsrc_traitcasting.vtable_vptr_slot
918-
}
919-
otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
920-
}
921-
}
922-
923566
pub fn provide(providers: &mut ty::query::Providers) {
924567
object_safety::provide(providers);
568+
vtable::provide(providers);
925569
*providers = ty::query::Providers {
926570
specialization_graph_of: specialize::specialization_graph_provider,
927571
specializes: specialize::specializes,
928572
codegen_select_candidate: codegen::codegen_select_candidate,
929-
own_existential_vtable_entries,
930-
vtable_entries,
931-
vtable_trait_upcasting_coercion_new_vptr_slot,
932573
subst_and_check_impossible_predicates,
933574
is_impossible_method,
934575
..*providers

0 commit comments

Comments
 (0)