rustc_smir/stable_mir/mir/
mono.rs

1use std::fmt::{Debug, Formatter};
2use std::io;
3
4use serde::Serialize;
5use stable_mir::abi::FnAbi;
6use stable_mir::crate_def::CrateDef;
7use stable_mir::mir::Body;
8use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
9use stable_mir::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with};
10
11use crate::stable_mir;
12
13#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
14pub enum MonoItem {
15    Fn(Instance),
16    Static(StaticDef),
17    GlobalAsm(Opaque),
18}
19
20#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize)]
21pub struct Instance {
22    /// The type of instance.
23    pub kind: InstanceKind,
24    /// An ID used to get the instance definition from the compiler.
25    /// Do not use this field directly.
26    pub def: InstanceDef,
27}
28
29#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
30pub enum InstanceKind {
31    /// A user defined item.
32    Item,
33    /// A compiler intrinsic function.
34    Intrinsic,
35    /// A virtual function definition stored in a VTable.
36    /// The `idx` field indicates the position in the VTable for this instance.
37    Virtual { idx: usize },
38    /// A compiler generated shim.
39    Shim,
40}
41
42impl Instance {
43    /// Get the arguments this instance was instantiated with.
44    pub fn args(&self) -> GenericArgs {
45        with(|cx| cx.instance_args(self.def))
46    }
47
48    /// Get the body of an Instance.
49    ///
50    /// The body will be eagerly monomorphized and all constants will already be evaluated.
51    ///
52    /// This method will return the intrinsic fallback body if one was defined.
53    pub fn body(&self) -> Option<Body> {
54        with(|context| context.instance_body(self.def))
55    }
56
57    /// Check whether this instance has a body available.
58    ///
59    /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide
60    /// whether to specialize the intrinsic or to use its fallback body.
61    ///
62    /// For more information on fallback body, see <https://github.com/rust-lang/rust/issues/93145>.
63    ///
64    /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build
65    /// the StableMIR body.
66    pub fn has_body(&self) -> bool {
67        with(|cx| cx.has_body(self.def.def_id()))
68    }
69
70    pub fn is_foreign_item(&self) -> bool {
71        with(|cx| cx.is_foreign_item(self.def.def_id()))
72    }
73
74    /// Get the instance type with generic instantiations applied and lifetimes erased.
75    pub fn ty(&self) -> Ty {
76        with(|context| context.instance_ty(self.def))
77    }
78
79    /// Retrieve information about this instance binary interface.
80    pub fn fn_abi(&self) -> Result<FnAbi, Error> {
81        with(|cx| cx.instance_abi(self.def))
82    }
83
84    /// Retrieve the instance's mangled name used for calling the given instance.
85    ///
86    /// This will also look up the correct name of instances from upstream crates.
87    pub fn mangled_name(&self) -> Symbol {
88        with(|context| context.instance_mangled_name(self.def))
89    }
90
91    /// Retrieve the instance name for diagnostic messages.
92    ///
93    /// This will return the specialized name, e.g., `std::vec::Vec<u8>::new`.
94    pub fn name(&self) -> Symbol {
95        with(|context| context.instance_name(self.def, false))
96    }
97
98    /// Return a trimmed name of the given instance including its args.
99    ///
100    /// If a symbol name can only be imported from one place for a type, and as
101    /// long as it was not glob-imported anywhere in the current crate, we trim its
102    /// path and print only the name.
103    pub fn trimmed_name(&self) -> Symbol {
104        with(|context| context.instance_name(self.def, true))
105    }
106
107    /// Retrieve the plain intrinsic name of an instance if it's an intrinsic.
108    ///
109    /// The plain name does not include type arguments (as `trimmed_name` does),
110    /// which is more convenient to match with intrinsic symbols.
111    pub fn intrinsic_name(&self) -> Option<Symbol> {
112        match self.kind {
113            InstanceKind::Intrinsic => {
114                Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name()))
115            }
116            InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None,
117        }
118    }
119
120    /// Resolve an instance starting from a function definition and generic arguments.
121    pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, Error> {
122        with(|context| {
123            context
124                .resolve_instance(def, args)
125                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
126        })
127    }
128
129    /// Resolve the drop in place for a given type.
130    pub fn resolve_drop_in_place(ty: Ty) -> Instance {
131        with(|cx| cx.resolve_drop_in_place(ty))
132    }
133
134    /// Resolve an instance for a given function pointer.
135    pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result<Instance, Error> {
136        with(|context| {
137            context
138                .resolve_for_fn_ptr(def, args)
139                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
140        })
141    }
142
143    /// Resolve a closure with the expected kind.
144    pub fn resolve_closure(
145        def: ClosureDef,
146        args: &GenericArgs,
147        kind: ClosureKind,
148    ) -> Result<Instance, Error> {
149        with(|context| {
150            context
151                .resolve_closure(def, args, kind)
152                .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")))
153        })
154    }
155
156    /// Check whether this instance is an empty shim.
157    ///
158    /// Allow users to check if this shim can be ignored when called directly.
159    ///
160    /// We have decided not to export different types of Shims to StableMIR users, however, this
161    /// is a query that can be very helpful for users when processing DropGlue.
162    ///
163    /// When generating code for a Drop terminator, users can ignore an empty drop glue.
164    /// These shims are only needed to generate a valid Drop call done via VTable.
165    pub fn is_empty_shim(&self) -> bool {
166        self.kind == InstanceKind::Shim
167            && with(|cx| {
168                cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def)
169            })
170    }
171
172    /// Try to constant evaluate the instance into a constant with the given type.
173    ///
174    /// This can be used to retrieve a constant that represents an intrinsic return such as
175    /// `type_id`.
176    pub fn try_const_eval(&self, const_ty: Ty) -> Result<Allocation, Error> {
177        with(|cx| cx.eval_instance(self.def, const_ty))
178    }
179
180    /// Emit the body of this instance if it has one.
181    pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
182        if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) }
183    }
184}
185
186impl Debug for Instance {
187    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
188        f.debug_struct("Instance")
189            .field("kind", &self.kind)
190            .field("def", &self.mangled_name())
191            .field("args", &self.args())
192            .finish()
193    }
194}
195
196/// Try to convert a crate item into an instance.
197/// The item cannot be generic in order to be converted into an instance.
198impl TryFrom<CrateItem> for Instance {
199    type Error = stable_mir::Error;
200
201    fn try_from(item: CrateItem) -> Result<Self, Self::Error> {
202        with(|context| {
203            let def_id = item.def_id();
204            if !context.requires_monomorphization(def_id) {
205                Ok(context.mono_instance(def_id))
206            } else {
207                Err(Error::new("Item requires monomorphization".to_string()))
208            }
209        })
210    }
211}
212
213/// Try to convert an instance into a crate item.
214/// Only user defined instances can be converted.
215impl TryFrom<Instance> for CrateItem {
216    type Error = stable_mir::Error;
217
218    fn try_from(value: Instance) -> Result<Self, Self::Error> {
219        with(|context| {
220            if value.kind == InstanceKind::Item && context.has_body(value.def.def_id()) {
221                Ok(CrateItem(context.instance_def_id(value.def)))
222            } else {
223                Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind)))
224            }
225        })
226    }
227}
228
229impl From<Instance> for MonoItem {
230    fn from(value: Instance) -> Self {
231        MonoItem::Fn(value)
232    }
233}
234
235impl From<StaticDef> for MonoItem {
236    fn from(value: StaticDef) -> Self {
237        MonoItem::Static(value)
238    }
239}
240
241impl From<StaticDef> for CrateItem {
242    fn from(value: StaticDef) -> Self {
243        CrateItem(value.0)
244    }
245}
246
247#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
248pub struct InstanceDef(usize);
249
250impl CrateDef for InstanceDef {
251    fn def_id(&self) -> DefId {
252        with(|context| context.instance_def_id(*self))
253    }
254}
255
256crate_def! {
257    /// Holds information about a static variable definition.
258    #[derive(Serialize)]
259    pub StaticDef;
260}
261
262impl TryFrom<CrateItem> for StaticDef {
263    type Error = stable_mir::Error;
264
265    fn try_from(value: CrateItem) -> Result<Self, Self::Error> {
266        if matches!(value.kind(), ItemKind::Static) {
267            Ok(StaticDef(value.0))
268        } else {
269            Err(Error::new(format!("Expected a static item, but found: {value:?}")))
270        }
271    }
272}
273
274impl TryFrom<Instance> for StaticDef {
275    type Error = stable_mir::Error;
276
277    fn try_from(value: Instance) -> Result<Self, Self::Error> {
278        StaticDef::try_from(CrateItem::try_from(value)?)
279    }
280}
281
282impl From<StaticDef> for Instance {
283    fn from(value: StaticDef) -> Self {
284        // A static definition should always be convertible to an instance.
285        with(|cx| cx.mono_instance(value.def_id()))
286    }
287}
288
289impl StaticDef {
290    /// Return the type of this static definition.
291    pub fn ty(&self) -> Ty {
292        with(|cx| cx.def_ty(self.0))
293    }
294
295    /// Evaluate a static's initializer, returning the allocation of the initializer's memory.
296    pub fn eval_initializer(&self) -> Result<Allocation, Error> {
297        with(|cx| cx.eval_static_initializer(*self))
298    }
299}
300
301impl IndexedVal for InstanceDef {
302    fn to_val(index: usize) -> Self {
303        InstanceDef(index)
304    }
305    fn to_index(&self) -> usize {
306        self.0
307    }
308}