@@ -3,6 +3,8 @@ use std::fmt;
33use crate :: mir:: interpret:: { alloc_range, AllocId , Allocation , Pointer , Scalar } ;
44use crate :: ty:: { self , Instance , PolyTraitRef , Ty , TyCtxt } ;
55use rustc_ast:: Mutability ;
6+ use rustc_data_structures:: fx:: FxHashSet ;
7+ use rustc_hir:: def_id:: DefId ;
68
79#[ derive( Clone , Copy , PartialEq , HashStable ) ]
810pub enum VtblEntry < ' tcx > {
@@ -45,6 +47,65 @@ pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0;
4547pub const COMMON_VTABLE_ENTRIES_SIZE : usize = 1 ;
4648pub const COMMON_VTABLE_ENTRIES_ALIGN : usize = 2 ;
4749
50+ // FIXME: This is duplicating equivalent code in compiler/rustc_trait_selection/src/traits/util.rs
51+ // But that is a downstream crate, and this code is pretty simple. Probably OK for now.
52+ struct SupertraitDefIds < ' tcx > {
53+ tcx : TyCtxt < ' tcx > ,
54+ stack : Vec < DefId > ,
55+ visited : FxHashSet < DefId > ,
56+ }
57+
58+ fn supertrait_def_ids ( tcx : TyCtxt < ' _ > , trait_def_id : DefId ) -> SupertraitDefIds < ' _ > {
59+ SupertraitDefIds {
60+ tcx,
61+ stack : vec ! [ trait_def_id] ,
62+ visited : Some ( trait_def_id) . into_iter ( ) . collect ( ) ,
63+ }
64+ }
65+
66+ impl Iterator for SupertraitDefIds < ' _ > {
67+ type Item = DefId ;
68+
69+ fn next ( & mut self ) -> Option < DefId > {
70+ let def_id = self . stack . pop ( ) ?;
71+ let predicates = self . tcx . super_predicates_of ( def_id) ;
72+ let visited = & mut self . visited ;
73+ self . stack . extend (
74+ predicates
75+ . predicates
76+ . iter ( )
77+ . filter_map ( |( pred, _) | pred. as_trait_clause ( ) )
78+ . map ( |trait_ref| trait_ref. def_id ( ) )
79+ . filter ( |& super_def_id| visited. insert ( super_def_id) ) ,
80+ ) ;
81+ Some ( def_id)
82+ }
83+ }
84+
85+ // Note that we don't have access to a self type here, this has to be purely based on the trait (and
86+ // supertrait) definitions. That means we can't call into the same vtable_entries code since that
87+ // returns a specific instantiation (e.g., with Vacant slots when bounds aren't satisfied). The goal
88+ // here is to do a best-effort approximation without duplicating a lot of code.
89+ //
90+ // This function is used in layout computation for e.g. &dyn Trait, so it's critical that this
91+ // function is an accurate approximation. We verify this when actually computing the vtable below.
92+ pub ( crate ) fn vtable_min_entries < ' tcx > (
93+ tcx : TyCtxt < ' tcx > ,
94+ trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
95+ ) -> usize {
96+ let mut count = TyCtxt :: COMMON_VTABLE_ENTRIES . len ( ) ;
97+ let Some ( trait_ref) = trait_ref else {
98+ return count;
99+ } ;
100+
101+ // This includes self in supertraits.
102+ for def_id in supertrait_def_ids ( tcx, trait_ref. def_id ( ) ) {
103+ count += tcx. own_existential_vtable_entries ( def_id) . len ( ) ;
104+ }
105+
106+ count
107+ }
108+
48109/// Retrieves an allocation that represents the contents of a vtable.
49110/// Since this is a query, allocations are cached and not duplicated.
50111pub ( super ) fn vtable_allocation_provider < ' tcx > (
@@ -62,6 +123,9 @@ pub(super) fn vtable_allocation_provider<'tcx>(
62123 TyCtxt :: COMMON_VTABLE_ENTRIES
63124 } ;
64125
126+ // This confirms that the layout computation for &dyn Trait has an accurate sizing.
127+ assert ! ( vtable_entries. len( ) >= vtable_min_entries( tcx, poly_trait_ref) ) ;
128+
65129 let layout = tcx
66130 . layout_of ( ty:: ParamEnv :: reveal_all ( ) . and ( ty) )
67131 . expect ( "failed to build vtable representation" ) ;
0 commit comments