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    pub fn multi_projection_ty(
90        self,
91        tcx: TyCtxt<'tcx>,
92        elems: &[PlaceElem<'tcx>],
93    ) -> PlaceTy<'tcx> {
94        elems.iter().fold(self, |place_ty, &elem| place_ty.projection_ty(tcx, elem))
95    }
96
97    /// Convenience wrapper around `projection_ty_core` for
98    /// `PlaceElem`, where we can just use the `Ty` that is already
99    /// stored inline on field projection elems.
100    pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
101        self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty)
102    }
103
104    /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
105    /// projects `place_ty` onto `elem`, returning the appropriate
106    /// `Ty` or downcast variant corresponding to that projection.
107    /// The `handle_field` callback must map a `FieldIdx` to its `Ty`,
108    /// (which should be trivial when `T` = `Ty`).
109    pub fn projection_ty_core<V, T>(
110        self,
111        tcx: TyCtxt<'tcx>,
112        elem: &ProjectionElem<V, T>,
113        mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
114        mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
115    ) -> PlaceTy<'tcx>
116    where
117        V: ::std::fmt::Debug,
118        T: ::std::fmt::Debug + Copy,
119    {
120        if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
121            bug!("cannot use non field projection on downcasted place")
122        }
123        let answer = match *elem {
124            ProjectionElem::Deref => {
125                let ty = self.ty.builtin_deref(true).unwrap_or_else(|| {
126                    bug!("deref projection of non-dereferenceable ty {:?}", self)
127                });
128                PlaceTy::from_ty(ty)
129            }
130            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
131                PlaceTy::from_ty(self.ty.builtin_index().unwrap())
132            }
133            ProjectionElem::Subslice { from, to, from_end } => {
134                PlaceTy::from_ty(match self.ty.kind() {
135                    ty::Slice(..) => self.ty,
136                    ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
137                    ty::Array(inner, size) if from_end => {
138                        let size = size
139                            .try_to_target_usize(tcx)
140                            .expect("expected subslice projection on fixed-size array");
141                        let len = size - from - to;
142                        Ty::new_array(tcx, *inner, len)
143                    }
144                    _ => bug!("cannot subslice non-array type: `{:?}`", self),
145                })
146            }
147            ProjectionElem::Downcast(_name, index) => {
148                PlaceTy { ty: self.ty, variant_index: Some(index) }
149            }
150            ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
151            ProjectionElem::OpaqueCast(ty) => {
152                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
153            }
154            ProjectionElem::Subtype(ty) => {
155                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
156            }
157
158            // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.
159            ProjectionElem::UnwrapUnsafeBinder(ty) => {
160                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
161            }
162        };
163        debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
164        answer
165    }
166}
167
168impl<'tcx> Place<'tcx> {
169    pub fn ty_from<D: ?Sized>(
170        local: Local,
171        projection: &[PlaceElem<'tcx>],
172        local_decls: &D,
173        tcx: TyCtxt<'tcx>,
174    ) -> PlaceTy<'tcx>
175    where
176        D: HasLocalDecls<'tcx>,
177    {
178        PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection)
179    }
180
181    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
182    where
183        D: HasLocalDecls<'tcx>,
184    {
185        Place::ty_from(self.local, self.projection, local_decls, tcx)
186    }
187}
188
189impl<'tcx> PlaceRef<'tcx> {
190    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
191    where
192        D: HasLocalDecls<'tcx>,
193    {
194        Place::ty_from(self.local, self.projection, local_decls, tcx)
195    }
196}
197
198pub enum RvalueInitializationState {
199    Shallow,
200    Deep,
201}
202
203impl<'tcx> Rvalue<'tcx> {
204    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
205    where
206        D: HasLocalDecls<'tcx>,
207    {
208        match *self {
209            Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
210            Rvalue::Repeat(ref operand, count) => {
211                Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
212            }
213            Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
214            Rvalue::Ref(reg, bk, ref place) => {
215                let place_ty = place.ty(local_decls, tcx).ty;
216                Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy())
217            }
218            Rvalue::RawPtr(kind, ref place) => {
219                let place_ty = place.ty(local_decls, tcx).ty;
220                Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy())
221            }
222            Rvalue::Len(..) => tcx.types.usize,
223            Rvalue::Cast(.., ty) => ty,
224            Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
225                let lhs_ty = lhs.ty(local_decls, tcx);
226                let rhs_ty = rhs.ty(local_decls, tcx);
227                op.ty(tcx, lhs_ty, rhs_ty)
228            }
229            Rvalue::UnaryOp(op, ref operand) => {
230                let arg_ty = operand.ty(local_decls, tcx);
231                op.ty(tcx, arg_ty)
232            }
233            Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
234            Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
235                tcx.types.usize
236            }
237            Rvalue::NullaryOp(NullOp::ContractChecks, _)
238            | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
239            Rvalue::Aggregate(ref ak, ref ops) => match **ak {
240                AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
241                AggregateKind::Tuple => {
242                    Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
243                }
244                AggregateKind::Adt(did, _, args, _, _) => tcx.type_of(did).instantiate(tcx, args),
245                AggregateKind::Closure(did, args) => Ty::new_closure(tcx, did, args),
246                AggregateKind::Coroutine(did, args) => Ty::new_coroutine(tcx, did, args),
247                AggregateKind::CoroutineClosure(did, args) => {
248                    Ty::new_coroutine_closure(tcx, did, args)
249                }
250                AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
251            },
252            Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
253            Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
254            Rvalue::WrapUnsafeBinder(_, ty) => ty,
255        }
256    }
257
258    #[inline]
259    /// Returns `true` if this rvalue is deeply initialized (most rvalues) or
260    /// whether its only shallowly initialized (`Rvalue::Box`).
261    pub fn initialization_state(&self) -> RvalueInitializationState {
262        match *self {
263            Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow,
264            _ => RvalueInitializationState::Deep,
265        }
266    }
267}
268
269impl<'tcx> Operand<'tcx> {
270    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
271    where
272        D: HasLocalDecls<'tcx>,
273    {
274        match self {
275            &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
276            Operand::Constant(c) => c.const_.ty(),
277        }
278    }
279
280    pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span
281    where
282        D: HasLocalDecls<'tcx>,
283    {
284        match self {
285            &Operand::Copy(ref l) | &Operand::Move(ref l) => {
286                local_decls.local_decls()[l.local].source_info.span
287            }
288            Operand::Constant(c) => c.span,
289        }
290    }
291}
292
293impl<'tcx> BinOp {
294    pub fn ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx> {
295        // FIXME: handle SIMD correctly
296        match self {
297            &BinOp::Add
298            | &BinOp::AddUnchecked
299            | &BinOp::Sub
300            | &BinOp::SubUnchecked
301            | &BinOp::Mul
302            | &BinOp::MulUnchecked
303            | &BinOp::Div
304            | &BinOp::Rem
305            | &BinOp::BitXor
306            | &BinOp::BitAnd
307            | &BinOp::BitOr => {
308                // these should be integers or floats of the same size.
309                assert_eq!(lhs_ty, rhs_ty);
310                lhs_ty
311            }
312            &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => {
313                // these should be integers of the same size.
314                assert_eq!(lhs_ty, rhs_ty);
315                Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool])
316            }
317            &BinOp::Shl
318            | &BinOp::ShlUnchecked
319            | &BinOp::Shr
320            | &BinOp::ShrUnchecked
321            | &BinOp::Offset => {
322                lhs_ty // lhs_ty can be != rhs_ty
323            }
324            &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
325                tcx.types.bool
326            }
327            &BinOp::Cmp => {
328                // these should be integer-like types of the same size.
329                assert_eq!(lhs_ty, rhs_ty);
330                tcx.ty_ordering_enum(None)
331            }
332        }
333    }
334}
335
336impl<'tcx> UnOp {
337    pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
338        match self {
339            UnOp::Not | UnOp::Neg => arg_ty,
340            UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx),
341        }
342    }
343}
344
345impl BorrowKind {
346    pub fn to_mutbl_lossy(self) -> hir::Mutability {
347        match self {
348            BorrowKind::Mut { .. } => hir::Mutability::Mut,
349            BorrowKind::Shared => hir::Mutability::Not,
350
351            // We have no type corresponding to a shallow borrow, so use
352            // `&` as an approximation.
353            BorrowKind::Fake(_) => hir::Mutability::Not,
354        }
355    }
356}
357
358impl BinOp {
359    pub(crate) fn to_hir_binop(self) -> hir::BinOpKind {
360        match self {
361            // HIR `+`/`-`/`*` can map to either of these MIR BinOp, depending
362            // on whether overflow checks are enabled or not.
363            BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add,
364            BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub,
365            BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul,
366            BinOp::Div => hir::BinOpKind::Div,
367            BinOp::Rem => hir::BinOpKind::Rem,
368            BinOp::BitXor => hir::BinOpKind::BitXor,
369            BinOp::BitAnd => hir::BinOpKind::BitAnd,
370            BinOp::BitOr => hir::BinOpKind::BitOr,
371            BinOp::Shl => hir::BinOpKind::Shl,
372            BinOp::Shr => hir::BinOpKind::Shr,
373            BinOp::Eq => hir::BinOpKind::Eq,
374            BinOp::Ne => hir::BinOpKind::Ne,
375            BinOp::Lt => hir::BinOpKind::Lt,
376            BinOp::Gt => hir::BinOpKind::Gt,
377            BinOp::Le => hir::BinOpKind::Le,
378            BinOp::Ge => hir::BinOpKind::Ge,
379            // We don't have HIR syntax for these.
380            BinOp::Cmp
381            | BinOp::AddUnchecked
382            | BinOp::SubUnchecked
383            | BinOp::MulUnchecked
384            | BinOp::ShlUnchecked
385            | BinOp::ShrUnchecked
386            | BinOp::Offset => {
387                unreachable!()
388            }
389        }
390    }
391
392    /// If this is a `FooWithOverflow`, return `Some(Foo)`.
393    pub fn overflowing_to_wrapping(self) -> Option<BinOp> {
394        Some(match self {
395            BinOp::AddWithOverflow => BinOp::Add,
396            BinOp::SubWithOverflow => BinOp::Sub,
397            BinOp::MulWithOverflow => BinOp::Mul,
398            _ => return None,
399        })
400    }
401
402    /// Returns whether this is a `FooWithOverflow`
403    pub fn is_overflowing(self) -> bool {
404        self.overflowing_to_wrapping().is_some()
405    }
406
407    /// If this is a `Foo`, return `Some(FooWithOverflow)`.
408    pub fn wrapping_to_overflowing(self) -> Option<BinOp> {
409        Some(match self {
410            BinOp::Add => BinOp::AddWithOverflow,
411            BinOp::Sub => BinOp::SubWithOverflow,
412            BinOp::Mul => BinOp::MulWithOverflow,
413            _ => return None,
414        })
415    }
416}