@@ -24,8 +24,18 @@ pub enum VtblSegment<'tcx> {
24
24
pub fn prepare_vtable_segments < ' tcx , T > (
25
25
tcx : TyCtxt < ' tcx > ,
26
26
trait_ref : ty:: PolyTraitRef < ' tcx > ,
27
- mut segment_visitor : impl FnMut ( VtblSegment < ' tcx > ) -> ControlFlow < T > ,
27
+ segment_visitor : impl FnMut ( VtblSegment < ' tcx > ) -> ControlFlow < T > ,
28
28
) -> Option < T > {
29
+ prepare_vtable_segments_inner ( tcx, trait_ref, segment_visitor) . break_value ( )
30
+ }
31
+
32
+ /// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`,
33
+ /// such that we can use `?` in the body.
34
+ fn prepare_vtable_segments_inner < ' tcx , T > (
35
+ tcx : TyCtxt < ' tcx > ,
36
+ trait_ref : ty:: PolyTraitRef < ' tcx > ,
37
+ mut segment_visitor : impl FnMut ( VtblSegment < ' tcx > ) -> ControlFlow < T > ,
38
+ ) -> ControlFlow < T > {
29
39
// The following constraints holds for the final arrangement.
30
40
// 1. The whole virtual table of the first direct super trait is included as the
31
41
// the prefix. If this trait doesn't have any super traits, then this step
@@ -71,20 +81,18 @@ pub fn prepare_vtable_segments<'tcx, T>(
71
81
// N, N-vptr, O
72
82
73
83
// emit dsa segment first.
74
- if let ControlFlow :: Break ( v) = ( segment_visitor) ( VtblSegment :: MetadataDSA ) {
75
- return Some ( v) ;
76
- }
84
+ segment_visitor ( VtblSegment :: MetadataDSA ) ?;
77
85
78
86
let mut emit_vptr_on_new_entry = false ;
79
87
let mut visited = PredicateSet :: new ( tcx) ;
80
88
let predicate = trait_ref. without_const ( ) . to_predicate ( tcx) ;
81
89
let mut stack: SmallVec < [ ( ty:: PolyTraitRef < ' tcx > , _ , _ ) ; 5 ] > =
82
- smallvec ! [ ( trait_ref, emit_vptr_on_new_entry, None ) ] ;
90
+ smallvec ! [ ( trait_ref, emit_vptr_on_new_entry, maybe_iter ( None ) ) ] ;
83
91
visited. insert ( predicate) ;
84
92
85
93
// the main traversal loop:
86
94
// basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
87
- // that each node is emitted after all its descendents have been emitted.
95
+ // such that each node is emitted after all its descendants have been emitted.
88
96
// so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
89
97
// this is done on the fly.
90
98
// Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
@@ -105,80 +113,81 @@ pub fn prepare_vtable_segments<'tcx, T>(
105
113
// Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
106
114
// Loop run #1: Stack after exiting out is []. Now the function exits.
107
115
108
- loop {
116
+ ' outer : loop {
109
117
// dive deeper into the stack, recording the path
110
118
' diving_in: loop {
111
- if let Some ( ( inner_most_trait_ref, _, _) ) = stack. last ( ) {
112
- let inner_most_trait_ref = * inner_most_trait_ref ;
113
- let mut direct_super_traits_iter = tcx
114
- . super_predicates_of ( inner_most_trait_ref. def_id ( ) )
115
- . predicates
116
- . into_iter ( )
117
- . filter_map ( move |( pred, _) | {
118
- pred. subst_supertrait ( tcx, & inner_most_trait_ref) . as_trait_clause ( )
119
- } ) ;
119
+ let & ( inner_most_trait_ref, _, _) = stack. last ( ) . unwrap ( ) ;
120
+
121
+ let mut direct_super_traits_iter = tcx
122
+ . super_predicates_of ( inner_most_trait_ref. def_id ( ) )
123
+ . predicates
124
+ . into_iter ( )
125
+ . filter_map ( move |( pred, _) | {
126
+ pred. subst_supertrait ( tcx, & inner_most_trait_ref) . as_trait_clause ( )
127
+ } ) ;
120
128
121
- ' diving_in_skip_visited_traits: loop {
122
- if let Some ( next_super_trait) = direct_super_traits_iter. next ( ) {
123
- if visited. insert ( next_super_trait. to_predicate ( tcx) ) {
124
- // We're throwing away potential constness of super traits here.
125
- // FIXME: handle ~const super traits
126
- let next_super_trait = next_super_trait. map_bound ( |t| t. trait_ref ) ;
127
- stack. push ( (
128
- next_super_trait,
129
- emit_vptr_on_new_entry,
130
- Some ( direct_super_traits_iter) ,
131
- ) ) ;
132
- break ' diving_in_skip_visited_traits;
133
- } else {
134
- continue ' diving_in_skip_visited_traits;
135
- }
136
- } else {
137
- break ' diving_in;
138
- }
129
+ // Find an unvisited supertrait
130
+ match direct_super_traits_iter
131
+ . find ( |& super_trait| visited. insert ( super_trait. to_predicate ( tcx) ) )
132
+ {
133
+ // Push it to the stack for the next iteration of 'diving_in to pick up
134
+ Some ( unvisited_super_trait) => {
135
+ // We're throwing away potential constness of super traits here.
136
+ // FIXME: handle ~const super traits
137
+ let next_super_trait = unvisited_super_trait. map_bound ( |t| t. trait_ref ) ;
138
+ stack. push ( (
139
+ next_super_trait,
140
+ emit_vptr_on_new_entry,
141
+ maybe_iter ( Some ( direct_super_traits_iter) ) ,
142
+ ) )
139
143
}
144
+
145
+ // There are no more unvisited direct super traits, dive-in finished
146
+ None => break ' diving_in,
140
147
}
141
148
}
142
149
143
- // Other than the left-most path, vptr should be emitted for each trait.
144
- emit_vptr_on_new_entry = true ;
145
-
146
150
// emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
147
- ' exiting_out: loop {
148
- if let Some ( ( inner_most_trait_ref, emit_vptr, siblings_opt) ) = stack. last_mut ( ) {
149
- if let ControlFlow :: Break ( v) = ( segment_visitor) ( VtblSegment :: TraitOwnEntries {
150
- trait_ref : * inner_most_trait_ref,
151
- emit_vptr : * emit_vptr,
152
- } ) {
153
- return Some ( v) ;
154
- }
151
+ while let Some ( ( inner_most_trait_ref, emit_vptr, mut siblings) ) = stack. pop ( ) {
152
+ segment_visitor ( VtblSegment :: TraitOwnEntries {
153
+ trait_ref : inner_most_trait_ref,
154
+ emit_vptr,
155
+ } ) ?;
156
+
157
+ // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable,
158
+ // we'll need to emit vptrs from now on.
159
+ if !emit_vptr_on_new_entry
160
+ && has_own_existential_vtable_entries ( tcx, inner_most_trait_ref. def_id ( ) )
161
+ {
162
+ emit_vptr_on_new_entry = true ;
163
+ }
155
164
156
- ' exiting_out_skip_visited_traits: loop {
157
- if let Some ( siblings) = siblings_opt {
158
- if let Some ( next_inner_most_trait_ref) = siblings. next ( ) {
159
- if visited. insert ( next_inner_most_trait_ref. to_predicate ( tcx) ) {
160
- // We're throwing away potential constness of super traits here.
161
- // FIXME: handle ~const super traits
162
- let next_inner_most_trait_ref =
163
- next_inner_most_trait_ref. map_bound ( |t| t. trait_ref ) ;
164
- * inner_most_trait_ref = next_inner_most_trait_ref;
165
- * emit_vptr = emit_vptr_on_new_entry;
166
- break ' exiting_out;
167
- } else {
168
- continue ' exiting_out_skip_visited_traits;
169
- }
170
- }
171
- }
172
- stack. pop ( ) ;
173
- continue ' exiting_out;
174
- }
165
+ if let Some ( next_inner_most_trait_ref) =
166
+ siblings. find ( |& sibling| visited. insert ( sibling. to_predicate ( tcx) ) )
167
+ {
168
+ // We're throwing away potential constness of super traits here.
169
+ // FIXME: handle ~const super traits
170
+ let next_inner_most_trait_ref =
171
+ next_inner_most_trait_ref. map_bound ( |t| t. trait_ref ) ;
172
+
173
+ stack. push ( ( next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings) ) ;
174
+
175
+ // just pushed a new trait onto the stack, so we need to go through its super traits
176
+ continue ' outer;
175
177
}
176
- // all done
177
- return None ;
178
178
}
179
+
180
+ // the stack is empty, all done
181
+ return ControlFlow :: Continue ( ( ) ) ;
179
182
}
180
183
}
181
184
185
+ /// Turns option of iterator into an iterator (this is just flatten)
186
+ fn maybe_iter < I : Iterator > ( i : Option < I > ) -> impl Iterator < Item = I :: Item > {
187
+ // Flatten is bad perf-vise, we could probably implement a special case here that is better
188
+ i. into_iter ( ) . flatten ( )
189
+ }
190
+
182
191
fn dump_vtable_entries < ' tcx > (
183
192
tcx : TyCtxt < ' tcx > ,
184
193
sp : Span ,
@@ -192,11 +201,23 @@ fn dump_vtable_entries<'tcx>(
192
201
} ) ;
193
202
}
194
203
204
+ fn has_own_existential_vtable_entries ( tcx : TyCtxt < ' _ > , trait_def_id : DefId ) -> bool {
205
+ own_existential_vtable_entries_iter ( tcx, trait_def_id) . next ( ) . is_some ( )
206
+ }
207
+
195
208
fn own_existential_vtable_entries ( tcx : TyCtxt < ' _ > , trait_def_id : DefId ) -> & [ DefId ] {
209
+ tcx. arena . alloc_from_iter ( own_existential_vtable_entries_iter ( tcx, trait_def_id) )
210
+ }
211
+
212
+ fn own_existential_vtable_entries_iter (
213
+ tcx : TyCtxt < ' _ > ,
214
+ trait_def_id : DefId ,
215
+ ) -> impl Iterator < Item = DefId > + ' _ {
196
216
let trait_methods = tcx
197
217
. associated_items ( trait_def_id)
198
218
. in_definition_order ( )
199
219
. filter ( |item| item. kind == ty:: AssocKind :: Fn ) ;
220
+
200
221
// Now list each method's DefId (for within its trait).
201
222
let own_entries = trait_methods. filter_map ( move |& trait_method| {
202
223
debug ! ( "own_existential_vtable_entry: trait_method={:?}" , trait_method) ;
@@ -211,7 +232,7 @@ fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[Def
211
232
Some ( def_id)
212
233
} ) ;
213
234
214
- tcx . arena . alloc_from_iter ( own_entries. into_iter ( ) )
235
+ own_entries
215
236
}
216
237
217
238
/// Given a trait `trait_ref`, iterates the vtable entries
0 commit comments