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