rustc_middle/mir/
tcx.rs

1/*!
2 * Methods for the various MIR types. These are intended for use after
3 * building is complete.
4 */
5
6use rustc_hir as hir;
7use tracing::{debug, instrument};
8use ty::CoroutineArgsExt;
9
10use crate::mir::*;
11
12#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
13pub struct PlaceTy<'tcx> {
14    pub ty: Ty<'tcx>,
15    /// Downcast to a particular variant of an enum or a coroutine, if included.
16    pub variant_index: Option<VariantIdx>,
17}
18
19// At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers.
20#[cfg(target_pointer_width = "64")]
21rustc_data_structures::static_assert_size!(PlaceTy<'_>, 16);
22
23impl<'tcx> PlaceTy<'tcx> {
24    #[inline]
25    pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> {
26        PlaceTy { ty, variant_index: None }
27    }
28
29    /// `place_ty.field_ty(tcx, f)` computes the type of a given field.
30    ///
31    /// Most clients of `PlaceTy` can instead just extract the relevant type
32    /// directly from their `PlaceElem`, but some instances of `ProjectionElem<V, T>`
33    /// do not carry a `Ty` for `T`.
34    ///
35    /// Note that the resulting type has not been normalized.
36    #[instrument(level = "debug", skip(tcx), ret)]
37    pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> {
38        if let Some(variant_index) = self.variant_index {
39            match *self.ty.kind() {
40                ty::Adt(adt_def, args) if adt_def.is_enum() => {
41                    adt_def.variant(variant_index).fields[f].ty(tcx, args)
42                }
43                ty::Coroutine(def_id, args) => {
44                    let mut variants = args.as_coroutine().state_tys(def_id, tcx);
45                    let Some(mut variant) = variants.nth(variant_index.into()) else {
46                        bug!("variant {variant_index:?} of coroutine out of range: {self:?}");
47                    };
48
49                    variant
50                        .nth(f.index())
51                        .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}"))
52                }
53                _ => bug!("can't downcast non-adt non-coroutine type: {self:?}"),
54            }
55        } else {
56            match self.ty.kind() {
57                ty::Adt(adt_def, args) if !adt_def.is_enum() => {
58                    adt_def.non_enum_variant().fields[f].ty(tcx, args)
59                }
60                ty::Closure(_, args) => args
61                    .as_closure()
62                    .upvar_tys()
63                    .get(f.index())
64                    .copied()
65                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
66                ty::CoroutineClosure(_, args) => args
67                    .as_coroutine_closure()
68                    .upvar_tys()
69                    .get(f.index())
70                    .copied()
71                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
72                // Only prefix fields (upvars and current state) are
73                // accessible without a variant index.
74                ty::Coroutine(_, args) => args
75                    .as_coroutine()
76                    .prefix_tys()
77                    .get(f.index())
78                    .copied()
79                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
80                ty::Tuple(tys) => tys
81                    .get(f.index())
82                    .copied()
83                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
84                _ => bug!("can't project out of {self:?}"),
85            }
86        }
87    }
88
89    /// Convenience wrapper around `projection_ty_core` for
90    /// `PlaceElem`, where we can just use the `Ty` that is already
91    /// stored inline on field projection elems.
92    pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
93        self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty)
94    }
95
96    /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
97    /// projects `place_ty` onto `elem`, returning the appropriate
98    /// `Ty` or downcast variant corresponding to that projection.
99    /// The `handle_field` callback must map a `FieldIdx` to its `Ty`,
100    /// (which should be trivial when `T` = `Ty`).
101    pub fn projection_ty_core<V, T>(
102        self,
103        tcx: TyCtxt<'tcx>,
104        elem: &ProjectionElem<V, T>,
105        mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
106        mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
107    ) -> PlaceTy<'tcx>
108    where
109        V: ::std::fmt::Debug,
110        T: ::std::fmt::Debug + Copy,
111    {
112        if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
113            bug!("cannot use non field projection on downcasted place")
114        }
115        let answer = match *elem {
116            ProjectionElem::Deref => {
117                let ty = self.ty.builtin_deref(true).unwrap_or_else(|| {
118                    bug!("deref projection of non-dereferenceable ty {:?}", self)
119                });
120                PlaceTy::from_ty(ty)
121            }
122            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
123                PlaceTy::from_ty(self.ty.builtin_index().unwrap())
124            }
125            ProjectionElem::Subslice { from, to, from_end } => {
126                PlaceTy::from_ty(match self.ty.kind() {
127                    ty::Slice(..) => self.ty,
128                    ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
129                    ty::Array(inner, size) if from_end => {
130                        let size = size
131                            .try_to_target_usize(tcx)
132                            .expect("expected subslice projection on fixed-size array");
133                        let len = size - from - to;
134                        Ty::new_array(tcx, *inner, len)
135                    }
136                    _ => bug!("cannot subslice non-array type: `{:?}`", self),
137                })
138            }
139            ProjectionElem::Downcast(_name, index) => {
140                PlaceTy { ty: self.ty, variant_index: Some(index) }
141            }
142            ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
143            ProjectionElem::OpaqueCast(ty) => {
144                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
145            }
146            ProjectionElem::Subtype(ty) => {
147                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
148            }
149
150            // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.
151            ProjectionElem::UnwrapUnsafeBinder(ty) => {
152                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
153            }
154        };
155        debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
156        answer
157    }
158}
159
160impl<'tcx> Place<'tcx> {
161    pub fn ty_from<D: ?Sized>(
162        local: Local,
163        projection: &[PlaceElem<'tcx>],
164        local_decls: &D,
165        tcx: TyCtxt<'tcx>,
166    ) -> PlaceTy<'tcx>
167    where
168        D: HasLocalDecls<'tcx>,
169    {
170        projection
171            .iter()
172            .fold(PlaceTy::from_ty(local_decls.local_decls()[local].ty), |place_ty, &elem| {
173                place_ty.projection_ty(tcx, elem)
174            })
175    }
176
177    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
178    where
179        D: HasLocalDecls<'tcx>,
180    {
181        Place::ty_from(self.local, self.projection, local_decls, tcx)
182    }
183}
184
185impl<'tcx> PlaceRef<'tcx> {
186    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
187    where
188        D: HasLocalDecls<'tcx>,
189    {
190        Place::ty_from(self.local, self.projection, local_decls, tcx)
191    }
192}
193
194pub enum RvalueInitializationState {
195    Shallow,
196    Deep,
197}
198
199impl<'tcx> Rvalue<'tcx> {
200    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
201    where
202        D: HasLocalDecls<'tcx>,
203    {
204        match *self {
205            Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
206            Rvalue::Repeat(ref operand, count) => {
207                Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
208            }
209            Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
210            Rvalue::Ref(reg, bk, ref place) => {
211                let place_ty = place.ty(local_decls, tcx).ty;
212                Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy())
213            }
214            Rvalue::RawPtr(kind, ref place) => {
215                let place_ty = place.ty(local_decls, tcx).ty;
216                Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy())
217            }
218            Rvalue::Len(..) => tcx.types.usize,
219            Rvalue::Cast(.., ty) => ty,
220            Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
221                let lhs_ty = lhs.ty(local_decls, tcx);
222                let rhs_ty = rhs.ty(local_decls, tcx);
223                op.ty(tcx, lhs_ty, rhs_ty)
224            }
225            Rvalue::UnaryOp(op, ref operand) => {
226                let arg_ty = operand.ty(local_decls, tcx);
227                op.ty(tcx, arg_ty)
228            }
229            Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
230            Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
231                tcx.types.usize
232            }
233            Rvalue::NullaryOp(NullOp::ContractChecks, _)
234            | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
235            Rvalue::Aggregate(ref ak, ref ops) => match **ak {
236                AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
237                AggregateKind::Tuple => {
238                    Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
239                }
240                AggregateKind::Adt(did, _, args, _, _) => tcx.type_of(did).instantiate(tcx, args),
241                AggregateKind::Closure(did, args) => Ty::new_closure(tcx, did, args),
242                AggregateKind::Coroutine(did, args) => Ty::new_coroutine(tcx, did, args),
243                AggregateKind::CoroutineClosure(did, args) => {
244                    Ty::new_coroutine_closure(tcx, did, args)
245                }
246                AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
247            },
248            Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
249            Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
250            Rvalue::WrapUnsafeBinder(_, ty) => ty,
251        }
252    }
253
254    #[inline]
255    /// Returns `true` if this rvalue is deeply initialized (most rvalues) or
256    /// whether its only shallowly initialized (`Rvalue::Box`).
257    pub fn initialization_state(&self) -> RvalueInitializationState {
258        match *self {
259            Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow,
260            _ => RvalueInitializationState::Deep,
261        }
262    }
263}
264
265impl<'tcx> Operand<'tcx> {
266    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
267    where
268        D: HasLocalDecls<'tcx>,
269    {
270        match self {
271            &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
272            Operand::Constant(c) => c.const_.ty(),
273        }
274    }
275
276    pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span
277    where
278        D: HasLocalDecls<'tcx>,
279    {
280        match self {
281            &Operand::Copy(ref l) | &Operand::Move(ref l) => {
282                local_decls.local_decls()[l.local].source_info.span
283            }
284            Operand::Constant(c) => c.span,
285        }
286    }
287}
288
289impl<'tcx> BinOp {
290    pub fn ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx> {
291        // FIXME: handle SIMD correctly
292        match self {
293            &BinOp::Add
294            | &BinOp::AddUnchecked
295            | &BinOp::Sub
296            | &BinOp::SubUnchecked
297            | &BinOp::Mul
298            | &BinOp::MulUnchecked
299            | &BinOp::Div
300            | &BinOp::Rem
301            | &BinOp::BitXor
302            | &BinOp::BitAnd
303            | &BinOp::BitOr => {
304                // these should be integers or floats of the same size.
305                assert_eq!(lhs_ty, rhs_ty);
306                lhs_ty
307            }
308            &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => {
309                // these should be integers of the same size.
310                assert_eq!(lhs_ty, rhs_ty);
311                Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool])
312            }
313            &BinOp::Shl
314            | &BinOp::ShlUnchecked
315            | &BinOp::Shr
316            | &BinOp::ShrUnchecked
317            | &BinOp::Offset => {
318                lhs_ty // lhs_ty can be != rhs_ty
319            }
320            &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
321                tcx.types.bool
322            }
323            &BinOp::Cmp => {
324                // these should be integer-like types of the same size.
325                assert_eq!(lhs_ty, rhs_ty);
326                tcx.ty_ordering_enum(None)
327            }
328        }
329    }
330}
331
332impl<'tcx> UnOp {
333    pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
334        match self {
335            UnOp::Not | UnOp::Neg => arg_ty,
336            UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx),
337        }
338    }
339}
340
341impl BorrowKind {
342    pub fn to_mutbl_lossy(self) -> hir::Mutability {
343        match self {
344            BorrowKind::Mut { .. } => hir::Mutability::Mut,
345            BorrowKind::Shared => hir::Mutability::Not,
346
347            // We have no type corresponding to a shallow borrow, so use
348            // `&` as an approximation.
349            BorrowKind::Fake(_) => hir::Mutability::Not,
350        }
351    }
352}
353
354impl BinOp {
355    pub(crate) fn to_hir_binop(self) -> hir::BinOpKind {
356        match self {
357            // HIR `+`/`-`/`*` can map to either of these MIR BinOp, depending
358            // on whether overflow checks are enabled or not.
359            BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add,
360            BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub,
361            BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul,
362            BinOp::Div => hir::BinOpKind::Div,
363            BinOp::Rem => hir::BinOpKind::Rem,
364            BinOp::BitXor => hir::BinOpKind::BitXor,
365            BinOp::BitAnd => hir::BinOpKind::BitAnd,
366            BinOp::BitOr => hir::BinOpKind::BitOr,
367            BinOp::Shl => hir::BinOpKind::Shl,
368            BinOp::Shr => hir::BinOpKind::Shr,
369            BinOp::Eq => hir::BinOpKind::Eq,
370            BinOp::Ne => hir::BinOpKind::Ne,
371            BinOp::Lt => hir::BinOpKind::Lt,
372            BinOp::Gt => hir::BinOpKind::Gt,
373            BinOp::Le => hir::BinOpKind::Le,
374            BinOp::Ge => hir::BinOpKind::Ge,
375            // We don't have HIR syntax for these.
376            BinOp::Cmp
377            | BinOp::AddUnchecked
378            | BinOp::SubUnchecked
379            | BinOp::MulUnchecked
380            | BinOp::ShlUnchecked
381            | BinOp::ShrUnchecked
382            | BinOp::Offset => {
383                unreachable!()
384            }
385        }
386    }
387
388    /// If this is a `FooWithOverflow`, return `Some(Foo)`.
389    pub fn overflowing_to_wrapping(self) -> Option<BinOp> {
390        Some(match self {
391            BinOp::AddWithOverflow => BinOp::Add,
392            BinOp::SubWithOverflow => BinOp::Sub,
393            BinOp::MulWithOverflow => BinOp::Mul,
394            _ => return None,
395        })
396    }
397
398    /// Returns whether this is a `FooWithOverflow`
399    pub fn is_overflowing(self) -> bool {
400        self.overflowing_to_wrapping().is_some()
401    }
402
403    /// If this is a `Foo`, return `Some(FooWithOverflow)`.
404    pub fn wrapping_to_overflowing(self) -> Option<BinOp> {
405        Some(match self {
406            BinOp::Add => BinOp::AddWithOverflow,
407            BinOp::Sub => BinOp::SubWithOverflow,
408            BinOp::Mul => BinOp::MulWithOverflow,
409            _ => return None,
410        })
411    }
412}