@@ -720,6 +720,8 @@ struct MirUsedCollector<'a, 'tcx> {
720
720
tcx : TyCtxt < ' tcx > ,
721
721
body : & ' a mir:: Body < ' tcx > ,
722
722
used_items : & ' a mut MonoItems < ' tcx > ,
723
+ /// See the comment in `collect_items_of_instance` for the purpose of this set.
724
+ used_mentioned_items : & ' a mut FxHashSet < MentionedItem < ' tcx > > ,
723
725
instance : Instance < ' tcx > ,
724
726
/// Spans for move size lints already emitted. Helps avoid duplicate lints.
725
727
move_size_spans : Vec < Span > ,
@@ -990,6 +992,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
990
992
match terminator. kind {
991
993
mir:: TerminatorKind :: Call { ref func, ref args, ref fn_span, .. } => {
992
994
let callee_ty = func. ty ( self . body , tcx) ;
995
+ // *Before* monomorphizing, record that we already handled this mention.
996
+ if let ty:: FnDef ( def_id, args) = callee_ty. kind ( ) {
997
+ self . used_mentioned_items . insert ( MentionedItem :: Fn ( * def_id, args) ) ;
998
+ }
993
999
let callee_ty = self . monomorphize ( callee_ty) ;
994
1000
self . check_fn_args_move_size ( callee_ty, args, * fn_span, location) ;
995
1001
visit_fn_use ( self . tcx , callee_ty, true , source, & mut self . used_items )
@@ -1635,10 +1641,22 @@ fn collect_items_of_instance<'tcx>(
1635
1641
mode : CollectionMode ,
1636
1642
) {
1637
1643
let body = tcx. instance_mir ( instance. def ) ;
1644
+ // Naively, in "used" collection mode, all functions get added to *both* `used_items` and
1645
+ // `mentioned_items`. Mentioned items processing will then notice that they have already been
1646
+ // visited, but at that point each mentioned item has been monomorphized, added to the
1647
+ // `mentioned_items` worklist, and checked in the global set of visited items. To removes that
1648
+ // overhead, we have a special optimization that avoids adding items to `mentioned_items` when
1649
+ // they are already added in `used_items`. We could just scan `used_items`, but that's a linear
1650
+ // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the
1651
+ // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already
1652
+ // added to `used_items` in a hash set, which can efficiently query in the
1653
+ // `body.mentioned_items` loop below.
1654
+ let mut used_mentioned_items = FxHashSet :: < MentionedItem < ' tcx > > :: default ( ) ;
1638
1655
let mut collector = MirUsedCollector {
1639
1656
tcx,
1640
1657
body,
1641
1658
used_items,
1659
+ used_mentioned_items : & mut used_mentioned_items,
1642
1660
instance,
1643
1661
move_size_spans : vec ! [ ] ,
1644
1662
visiting_call_terminator : false ,
@@ -1658,10 +1676,13 @@ fn collect_items_of_instance<'tcx>(
1658
1676
}
1659
1677
}
1660
1678
1661
- // Always gather mentioned items.
1679
+ // Always gather mentioned items. We try to avoid processing items that we have already added to
1680
+ // `used_items` above.
1662
1681
for item in & body. mentioned_items {
1663
- let item_mono = collector. monomorphize ( item. node ) ;
1664
- visit_mentioned_item ( tcx, & item_mono, item. span , mentioned_items) ;
1682
+ if !collector. used_mentioned_items . contains ( & item. node ) {
1683
+ let item_mono = collector. monomorphize ( item. node ) ;
1684
+ visit_mentioned_item ( tcx, & item_mono, item. span , mentioned_items) ;
1685
+ }
1665
1686
}
1666
1687
}
1667
1688
0 commit comments