@@ -24,8 +24,18 @@ pub enum VtblSegment<'tcx> {
2424pub fn prepare_vtable_segments < ' tcx , T > (
2525 tcx : TyCtxt < ' tcx > ,
2626 trait_ref : ty:: PolyTraitRef < ' tcx > ,
27- mut segment_visitor : impl FnMut ( VtblSegment < ' tcx > ) -> ControlFlow < T > ,
27+ segment_visitor : impl FnMut ( VtblSegment < ' tcx > ) -> ControlFlow < T > ,
2828) -> 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 > {
2939 // The following constraints holds for the final arrangement.
3040 // 1. The whole virtual table of the first direct super trait is included as the
3141 // 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>(
7181 // N, N-vptr, O
7282
7383 // emit dsa segment first.
74- if let ControlFlow :: Break ( v) = ( segment_visitor) ( VtblSegment :: MetadataDSA ) {
75- return Some ( v) ;
76- }
84+ segment_visitor ( VtblSegment :: MetadataDSA ) ?;
7785
7886 let mut emit_vptr_on_new_entry = false ;
7987 let mut visited = PredicateSet :: new ( tcx) ;
8088 let predicate = trait_ref. without_const ( ) . to_predicate ( tcx) ;
8189 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 ) ) ] ;
8391 visited. insert ( predicate) ;
8492
8593 // the main traversal loop:
8694 // 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.
8896 // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
8997 // this is done on the fly.
9098 // 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>(
105113 // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
106114 // Loop run #1: Stack after exiting out is []. Now the function exits.
107115
108- loop {
116+ ' outer : loop {
109117 // dive deeper into the stack, recording the path
110118 ' 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+ } ) ;
120128
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+ ) )
139143 }
144+
145+ // There are no more unvisited direct super traits, dive-in finished
146+ None => break ' diving_in,
140147 }
141148 }
142149
143- // Other than the left-most path, vptr should be emitted for each trait.
144- emit_vptr_on_new_entry = true ;
145-
146150 // 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+ }
155164
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;
175177 }
176- // all done
177- return None ;
178178 }
179+
180+ // the stack is empty, all done
181+ return ControlFlow :: Continue ( ( ) ) ;
179182 }
180183}
181184
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+
182191fn dump_vtable_entries < ' tcx > (
183192 tcx : TyCtxt < ' tcx > ,
184193 sp : Span ,
@@ -192,11 +201,23 @@ fn dump_vtable_entries<'tcx>(
192201 } ) ;
193202}
194203
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+
195208fn 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 > + ' _ {
196216 let trait_methods = tcx
197217 . associated_items ( trait_def_id)
198218 . in_definition_order ( )
199219 . filter ( |item| item. kind == ty:: AssocKind :: Fn ) ;
220+
200221 // Now list each method's DefId (for within its trait).
201222 let own_entries = trait_methods. filter_map ( move |& trait_method| {
202223 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
211232 Some ( def_id)
212233 } ) ;
213234
214- tcx . arena . alloc_from_iter ( own_entries. into_iter ( ) )
235+ own_entries
215236}
216237
217238/// Given a trait `trait_ref`, iterates the vtable entries
0 commit comments