rustc_mir_transform/
mentioned_items.rs

1use rustc_middle::mir::visit::Visitor;
2use rustc_middle::mir::{self, Location, MentionedItem};
3use rustc_middle::ty::adjustment::PointerCoercion;
4use rustc_middle::ty::{self, TyCtxt};
5use rustc_session::Session;
6use rustc_span::source_map::Spanned;
7
8pub(super) struct MentionedItems;
9
10struct MentionedItemsVisitor<'a, 'tcx> {
11    tcx: TyCtxt<'tcx>,
12    body: &'a mir::Body<'tcx>,
13    mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>,
14}
15
16impl<'tcx> crate::MirPass<'tcx> for MentionedItems {
17    fn is_enabled(&self, _sess: &Session) -> bool {
18        // If this pass is skipped the collector assume that nothing got mentioned! We could
19        // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses
20        // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so
21        // special-casing level 0 is just not worth it.
22        true
23    }
24
25    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
26        let mut visitor = MentionedItemsVisitor { tcx, body, mentioned_items: Vec::new() };
27        visitor.visit_body(body);
28        body.set_mentioned_items(visitor.mentioned_items);
29    }
30
31    fn is_required(&self) -> bool {
32        true
33    }
34}
35
36// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are
37// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we
38// have to remain generic and just recording the relevant information in `mentioned_items`, where it
39// will then be monomorphized later during "mentioned items" collection.
40impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
41    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
42        self.super_terminator(terminator, location);
43        let span = || self.body.source_info(location).span;
44        match &terminator.kind {
45            mir::TerminatorKind::Call { func, .. } | mir::TerminatorKind::TailCall { func, .. } => {
46                let callee_ty = func.ty(self.body, self.tcx);
47                self.mentioned_items
48                    .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() });
49            }
50            mir::TerminatorKind::Drop { place, .. } => {
51                let ty = place.ty(self.body, self.tcx).ty;
52                self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() });
53            }
54            mir::TerminatorKind::InlineAsm { ref operands, .. } => {
55                for op in operands {
56                    match *op {
57                        mir::InlineAsmOperand::SymFn { ref value } => {
58                            self.mentioned_items.push(Spanned {
59                                node: MentionedItem::Fn(value.const_.ty()),
60                                span: span(),
61                            });
62                        }
63                        _ => {}
64                    }
65                }
66            }
67            _ => {}
68        }
69    }
70
71    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
72        self.super_rvalue(rvalue, location);
73        let span = || self.body.source_info(location).span;
74        match *rvalue {
75            // We need to detect unsizing casts that required vtables.
76            mir::Rvalue::Cast(
77                mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _)
78                | mir::CastKind::PointerCoercion(PointerCoercion::DynStar, _),
79                ref operand,
80                target_ty,
81            ) => {
82                // This isn't monomorphized yet so we can't tell what the actual types are -- just
83                // add everything that may involve a vtable.
84                let source_ty = operand.ty(self.body, self.tcx);
85                let may_involve_vtable = match (
86                    source_ty.builtin_deref(true).map(|t| t.kind()),
87                    target_ty.builtin_deref(true).map(|t| t.kind()),
88                ) {
89                    // &str/&[T] unsizing
90                    (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false,
91
92                    _ => true,
93                };
94                if may_involve_vtable {
95                    self.mentioned_items.push(Spanned {
96                        node: MentionedItem::UnsizeCast { source_ty, target_ty },
97                        span: span(),
98                    });
99                }
100            }
101            // Similarly, record closures that are turned into function pointers.
102            mir::Rvalue::Cast(
103                mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _),
104                ref operand,
105                _,
106            ) => {
107                let source_ty = operand.ty(self.body, self.tcx);
108                self.mentioned_items
109                    .push(Spanned { node: MentionedItem::Closure(source_ty), span: span() });
110            }
111            // And finally, function pointer reification casts.
112            mir::Rvalue::Cast(
113                mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _),
114                ref operand,
115                _,
116            ) => {
117                let fn_ty = operand.ty(self.body, self.tcx);
118                self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() });
119            }
120            _ => {}
121        }
122    }
123}