Change:
// Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node. // Loop run #1: Stack after exiting out is []. Now the function exits. <del> loop { </del> <ins> 'outer: loop { </ins> // dive deeper into the stack, recording the path 'diving_in: loop { <del> if let Some((inner_most_trait_ref, _, _)) = stack.last() { let inner_most_trait_ref = *inner_most_trait_ref; let mut direct_super_traits_iter = tcx .super_predicates_of(inner_most_trait_ref.def_id()) .predicates .into_iter() .filter_map(move |(pred, _)| { pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() }); </del> <ins> let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); let mut direct_super_traits_iter = tcx .super_predicates_of(inner_most_trait_ref.def_id()) .predicates .into_iter() .filter_map(move |(pred, _)| { pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() }); </ins> <del> 'diving_in_skip_visited_traits: loop { if let Some(next_super_trait) = direct_super_traits_iter.next() { if visited.insert(next_super_trait.to_predicate(tcx)) { // We're throwing away potential constness of super traits here. // FIXME: handle ~const super traits let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); stack.push(( next_super_trait, emit_vptr_on_new_entry, Some(direct_super_traits_iter), )); break 'diving_in_skip_visited_traits; } else { continue 'diving_in_skip_visited_traits; } } else { break 'diving_in; } </del> <ins> // Find an unvisited supertrait match direct_super_traits_iter .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx))) { // Push it to the stack for the next iteration of 'diving_in to pick up Some(unvisited_super_trait) => { // We're throwing away potential constness of super traits here. // FIXME: handle ~const super traits let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); stack.push(( next_super_trait, emit_vptr_on_new_entry, maybe_iter(Some(direct_super_traits_iter)), )) </ins> } <ins> // There are no more unvisited direct super traits, dive-in finished None => break 'diving_in, </ins> } } <del> // Other than the left-most path, vptr should be emitted for each trait. emit_vptr_on_new_entry = true; </del> // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. <del> 'exiting_out: loop { if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() { if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries { trait_ref: *inner_most_trait_ref, emit_vptr: *emit_vptr, }) { return Some(v); } </del> <ins> while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: inner_most_trait_ref, emit_vptr, })?; // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, // we'll need to emit vptrs from now on. if !emit_vptr_on_new_entry && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) { emit_vptr_on_new_entry = true; } </ins> <del> 'exiting_out_skip_visited_traits: loop { if let Some(siblings) = siblings_opt { if let Some(next_inner_most_trait_ref) = siblings.next() { if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) { // We're throwing away potential constness of super traits here. // FIXME: handle ~const super traits let next_inner_most_trait_ref = next_inner_most_trait_ref.map_bound(|t| t.trait_ref); *inner_most_trait_ref = next_inner_most_trait_ref; *emit_vptr = emit_vptr_on_new_entry; break 'exiting_out; } else { continue 'exiting_out_skip_visited_traits; } } } stack.pop(); continue 'exiting_out; } </del> <ins> if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) { // We're throwing away potential constness of super traits here. // FIXME: handle ~const super traits let next_inner_most_trait_ref = next_inner_most_trait_ref.map_bound(|t| t.trait_ref); stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); // just pushed a new trait onto the stack, so we need to go through its super traits continue 'outer; </ins> } <del> // all done return None; </del> } <ins> // the stack is empty, all done return ControlFlow::Continue(()); </ins> } } <ins> /// Turns option of iterator into an iterator (this is just flatten) fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> { // Flatten is bad perf-vise, we could probably implement a special case here that is better i.into_iter().flatten() } </ins> fn dump_vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, sp: Span,

Discussion:
While working on I think I found a bug. Given this program: rustc outputs the following vtable layout: Which is surprising, because is unnecessary — does not have any methods, so vtable can be inlined... I think we should be smarter here: (for the record, I'm pretty sure this happens in practice, I've found this with ) Originally posted by in
cc
sorry been slow a little recently... This is a nice improvement.