@@ -3,6 +3,8 @@ use std::fmt;
3
3
use crate :: mir:: interpret:: { alloc_range, AllocId , Allocation , Pointer , Scalar } ;
4
4
use crate :: ty:: { self , Instance , PolyTraitRef , Ty , TyCtxt } ;
5
5
use rustc_ast:: Mutability ;
6
+ use rustc_data_structures:: fx:: FxHashSet ;
7
+ use rustc_hir:: def_id:: DefId ;
6
8
7
9
#[ derive( Clone , Copy , PartialEq , HashStable ) ]
8
10
pub enum VtblEntry < ' tcx > {
@@ -45,6 +47,65 @@ pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0;
45
47
pub const COMMON_VTABLE_ENTRIES_SIZE : usize = 1 ;
46
48
pub const COMMON_VTABLE_ENTRIES_ALIGN : usize = 2 ;
47
49
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
+
48
109
/// Retrieves an allocation that represents the contents of a vtable.
49
110
/// Since this is a query, allocations are cached and not duplicated.
50
111
pub ( super ) fn vtable_allocation_provider < ' tcx > (
@@ -62,6 +123,9 @@ pub(super) fn vtable_allocation_provider<'tcx>(
62
123
TyCtxt :: COMMON_VTABLE_ENTRIES
63
124
} ;
64
125
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
+
65
129
let layout = tcx
66
130
. layout_of ( ty:: ParamEnv :: reveal_all ( ) . and ( ty) )
67
131
. expect ( "failed to build vtable representation" ) ;
0 commit comments