Skip to main content

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