1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, Location, MentionedItem, MirPass};
use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt};
use rustc_session::Session;
use rustc_span::source_map::Spanned;

pub struct MentionedItems;

struct MentionedItemsVisitor<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    mentioned_items: &'a mut Vec<Spanned<MentionedItem<'tcx>>>,
}

impl<'tcx> MirPass<'tcx> for MentionedItems {
    fn is_enabled(&self, _sess: &Session) -> bool {
        // If this pass is skipped the collector assume that nothing got mentioned! We could
        // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses
        // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so
        // special-casing level 0 is just not worth it.
        true
    }

    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
        debug_assert!(body.mentioned_items.is_empty());
        let mut mentioned_items = Vec::new();
        MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body);
        body.mentioned_items = mentioned_items;
    }
}

// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are
// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we
// have to remain generic and just recording the relevant information in `mentioned_items`, where it
// will then be monomorphized later during "mentioned items" collection.
impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
        self.super_terminator(terminator, location);
        let span = || self.body.source_info(location).span;
        match &terminator.kind {
            mir::TerminatorKind::Call { func, .. } => {
                let callee_ty = func.ty(self.body, self.tcx);
                self.mentioned_items
                    .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() });
            }
            mir::TerminatorKind::Drop { place, .. } => {
                let ty = place.ty(self.body, self.tcx).ty;
                self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() });
            }
            mir::TerminatorKind::InlineAsm { ref operands, .. } => {
                for op in operands {
                    match *op {
                        mir::InlineAsmOperand::SymFn { ref value } => {
                            self.mentioned_items.push(Spanned {
                                node: MentionedItem::Fn(value.const_.ty()),
                                span: span(),
                            });
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
    }

    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
        self.super_rvalue(rvalue, location);
        let span = || self.body.source_info(location).span;
        match *rvalue {
            // We need to detect unsizing casts that required vtables.
            mir::Rvalue::Cast(
                mir::CastKind::PointerCoercion(PointerCoercion::Unsize),
                ref operand,
                target_ty,
            )
            | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => {
                // This isn't monomorphized yet so we can't tell what the actual types are -- just
                // add everything that may involve a vtable.
                let source_ty = operand.ty(self.body, self.tcx);
                let may_involve_vtable = match (
                    source_ty.builtin_deref(true).map(|t| t.kind()),
                    target_ty.builtin_deref(true).map(|t| t.kind()),
                ) {
                    (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false, // &str/&[T] unsizing
                    _ => true,
                };
                if may_involve_vtable {
                    self.mentioned_items.push(Spanned {
                        node: MentionedItem::UnsizeCast { source_ty, target_ty },
                        span: span(),
                    });
                }
            }
            // Similarly, record closures that are turned into function pointers.
            mir::Rvalue::Cast(
                mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
                ref operand,
                _,
            ) => {
                let source_ty = operand.ty(self.body, self.tcx);
                self.mentioned_items
                    .push(Spanned { node: MentionedItem::Closure(source_ty), span: span() });
            }
            // And finally, function pointer reification casts.
            mir::Rvalue::Cast(
                mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer),
                ref operand,
                _,
            ) => {
                let fn_ty = operand.ty(self.body, self.tcx);
                self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() });
            }
            _ => {}
        }
    }
}