rustc_middle/mir/
statement.rs

1//! Functionality for statements, operands, places, and things that appear in them.
2
3use tracing::{debug, instrument};
4
5use super::interpret::GlobalAlloc;
6use super::*;
7use crate::ty::CoroutineArgsExt;
8
9///////////////////////////////////////////////////////////////////////////
10// Statements
11
12/// A statement in a basic block, including information about its source code.
13#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
14pub struct Statement<'tcx> {
15    pub source_info: SourceInfo,
16    pub kind: StatementKind<'tcx>,
17}
18
19impl Statement<'_> {
20    /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
21    /// invalidating statement indices in `Location`s.
22    pub fn make_nop(&mut self) {
23        self.kind = StatementKind::Nop
24    }
25}
26
27impl<'tcx> StatementKind<'tcx> {
28    /// Returns a simple string representation of a `StatementKind` variant, independent of any
29    /// values it might hold (e.g. `StatementKind::Assign` always returns `"Assign"`).
30    pub const fn name(&self) -> &'static str {
31        match self {
32            StatementKind::Assign(..) => "Assign",
33            StatementKind::FakeRead(..) => "FakeRead",
34            StatementKind::SetDiscriminant { .. } => "SetDiscriminant",
35            StatementKind::Deinit(..) => "Deinit",
36            StatementKind::StorageLive(..) => "StorageLive",
37            StatementKind::StorageDead(..) => "StorageDead",
38            StatementKind::Retag(..) => "Retag",
39            StatementKind::PlaceMention(..) => "PlaceMention",
40            StatementKind::AscribeUserType(..) => "AscribeUserType",
41            StatementKind::Coverage(..) => "Coverage",
42            StatementKind::Intrinsic(..) => "Intrinsic",
43            StatementKind::ConstEvalCounter => "ConstEvalCounter",
44            StatementKind::Nop => "Nop",
45            StatementKind::BackwardIncompatibleDropHint { .. } => "BackwardIncompatibleDropHint",
46        }
47    }
48    pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
49        match self {
50            StatementKind::Assign(x) => Some(x),
51            _ => None,
52        }
53    }
54
55    pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
56        match self {
57            StatementKind::Assign(x) => Some(x),
58            _ => None,
59        }
60    }
61}
62
63///////////////////////////////////////////////////////////////////////////
64// Places
65
66#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
67pub struct PlaceTy<'tcx> {
68    pub ty: Ty<'tcx>,
69    /// Downcast to a particular variant of an enum or a coroutine, if included.
70    pub variant_index: Option<VariantIdx>,
71}
72
73// At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers.
74#[cfg(target_pointer_width = "64")]
75rustc_data_structures::static_assert_size!(PlaceTy<'_>, 16);
76
77impl<'tcx> PlaceTy<'tcx> {
78    #[inline]
79    pub fn from_ty(ty: Ty<'tcx>) -> PlaceTy<'tcx> {
80        PlaceTy { ty, variant_index: None }
81    }
82
83    /// `place_ty.field_ty(tcx, f)` computes the type of a given field.
84    ///
85    /// Most clients of `PlaceTy` can instead just extract the relevant type
86    /// directly from their `PlaceElem`, but some instances of `ProjectionElem<V, T>`
87    /// do not carry a `Ty` for `T`.
88    ///
89    /// Note that the resulting type has not been normalized.
90    #[instrument(level = "debug", skip(tcx), ret)]
91    pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> {
92        if let Some(variant_index) = self.variant_index {
93            match *self.ty.kind() {
94                ty::Adt(adt_def, args) if adt_def.is_enum() => {
95                    adt_def.variant(variant_index).fields[f].ty(tcx, args)
96                }
97                ty::Coroutine(def_id, args) => {
98                    let mut variants = args.as_coroutine().state_tys(def_id, tcx);
99                    let Some(mut variant) = variants.nth(variant_index.into()) else {
100                        bug!("variant {variant_index:?} of coroutine out of range: {self:?}");
101                    };
102
103                    variant
104                        .nth(f.index())
105                        .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}"))
106                }
107                _ => bug!("can't downcast non-adt non-coroutine type: {self:?}"),
108            }
109        } else {
110            match self.ty.kind() {
111                ty::Adt(adt_def, args) if !adt_def.is_enum() => {
112                    adt_def.non_enum_variant().fields[f].ty(tcx, args)
113                }
114                ty::Closure(_, args) => args
115                    .as_closure()
116                    .upvar_tys()
117                    .get(f.index())
118                    .copied()
119                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
120                ty::CoroutineClosure(_, args) => args
121                    .as_coroutine_closure()
122                    .upvar_tys()
123                    .get(f.index())
124                    .copied()
125                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
126                // Only prefix fields (upvars and current state) are
127                // accessible without a variant index.
128                ty::Coroutine(_, args) => args
129                    .as_coroutine()
130                    .prefix_tys()
131                    .get(f.index())
132                    .copied()
133                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
134                ty::Tuple(tys) => tys
135                    .get(f.index())
136                    .copied()
137                    .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
138                _ => bug!("can't project out of {self:?}"),
139            }
140        }
141    }
142
143    pub fn multi_projection_ty(
144        self,
145        tcx: TyCtxt<'tcx>,
146        elems: &[PlaceElem<'tcx>],
147    ) -> PlaceTy<'tcx> {
148        elems.iter().fold(self, |place_ty, &elem| place_ty.projection_ty(tcx, elem))
149    }
150
151    /// Convenience wrapper around `projection_ty_core` for
152    /// `PlaceElem`, where we can just use the `Ty` that is already
153    /// stored inline on field projection elems.
154    pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
155        self.projection_ty_core(tcx, &elem, |_, _, ty| ty, |_, ty| ty)
156    }
157
158    /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
159    /// projects `place_ty` onto `elem`, returning the appropriate
160    /// `Ty` or downcast variant corresponding to that projection.
161    /// The `handle_field` callback must map a `FieldIdx` to its `Ty`,
162    /// (which should be trivial when `T` = `Ty`).
163    pub fn projection_ty_core<V, T>(
164        self,
165        tcx: TyCtxt<'tcx>,
166        elem: &ProjectionElem<V, T>,
167        mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
168        mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
169    ) -> PlaceTy<'tcx>
170    where
171        V: ::std::fmt::Debug,
172        T: ::std::fmt::Debug + Copy,
173    {
174        if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
175            bug!("cannot use non field projection on downcasted place")
176        }
177        let answer = match *elem {
178            ProjectionElem::Deref => {
179                let ty = self.ty.builtin_deref(true).unwrap_or_else(|| {
180                    bug!("deref projection of non-dereferenceable ty {:?}", self)
181                });
182                PlaceTy::from_ty(ty)
183            }
184            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
185                PlaceTy::from_ty(self.ty.builtin_index().unwrap())
186            }
187            ProjectionElem::Subslice { from, to, from_end } => {
188                PlaceTy::from_ty(match self.ty.kind() {
189                    ty::Slice(..) => self.ty,
190                    ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
191                    ty::Array(inner, size) if from_end => {
192                        let size = size
193                            .try_to_target_usize(tcx)
194                            .expect("expected subslice projection on fixed-size array");
195                        let len = size - from - to;
196                        Ty::new_array(tcx, *inner, len)
197                    }
198                    _ => bug!("cannot subslice non-array type: `{:?}`", self),
199                })
200            }
201            ProjectionElem::Downcast(_name, index) => {
202                PlaceTy { ty: self.ty, variant_index: Some(index) }
203            }
204            ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
205            ProjectionElem::OpaqueCast(ty) => {
206                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
207            }
208            ProjectionElem::Subtype(ty) => {
209                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
210            }
211
212            // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.
213            ProjectionElem::UnwrapUnsafeBinder(ty) => {
214                PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
215            }
216        };
217        debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
218        answer
219    }
220}
221
222impl<V, T> ProjectionElem<V, T> {
223    /// Returns `true` if the target of this projection may refer to a different region of memory
224    /// than the base.
225    fn is_indirect(&self) -> bool {
226        match self {
227            Self::Deref => true,
228
229            Self::Field(_, _)
230            | Self::Index(_)
231            | Self::OpaqueCast(_)
232            | Self::Subtype(_)
233            | Self::ConstantIndex { .. }
234            | Self::Subslice { .. }
235            | Self::Downcast(_, _)
236            | Self::UnwrapUnsafeBinder(..) => false,
237        }
238    }
239
240    /// Returns `true` if the target of this projection always refers to the same memory region
241    /// whatever the state of the program.
242    pub fn is_stable_offset(&self) -> bool {
243        match self {
244            Self::Deref | Self::Index(_) => false,
245            Self::Field(_, _)
246            | Self::OpaqueCast(_)
247            | Self::Subtype(_)
248            | Self::ConstantIndex { .. }
249            | Self::Subslice { .. }
250            | Self::Downcast(_, _)
251            | Self::UnwrapUnsafeBinder(..) => true,
252        }
253    }
254
255    /// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
256    pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
257        matches!(*self, Self::Downcast(_, x) if x == v)
258    }
259
260    /// Returns `true` if this is a `Field` projection with the given index.
261    pub fn is_field_to(&self, f: FieldIdx) -> bool {
262        matches!(*self, Self::Field(x, _) if x == f)
263    }
264
265    /// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
266    pub fn can_use_in_debuginfo(&self) -> bool {
267        match self {
268            Self::ConstantIndex { from_end: false, .. }
269            | Self::Deref
270            | Self::Downcast(_, _)
271            | Self::Field(_, _) => true,
272            Self::ConstantIndex { from_end: true, .. }
273            | Self::Index(_)
274            | Self::Subtype(_)
275            | Self::OpaqueCast(_)
276            | Self::Subslice { .. } => false,
277
278            // FIXME(unsafe_binders): Figure this out.
279            Self::UnwrapUnsafeBinder(..) => false,
280        }
281    }
282}
283
284/// Alias for projections as they appear in `UserTypeProjection`, where we
285/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
286pub type ProjectionKind = ProjectionElem<(), ()>;
287
288#[derive(Clone, Copy, PartialEq, Eq, Hash)]
289pub struct PlaceRef<'tcx> {
290    pub local: Local,
291    pub projection: &'tcx [PlaceElem<'tcx>],
292}
293
294// Once we stop implementing `Ord` for `DefId`,
295// this impl will be unnecessary. Until then, we'll
296// leave this impl in place to prevent re-adding a
297// dependency on the `Ord` impl for `DefId`
298impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
299
300impl<'tcx> Place<'tcx> {
301    // FIXME change this to a const fn by also making List::empty a const fn.
302    pub fn return_place() -> Place<'tcx> {
303        Place { local: RETURN_PLACE, projection: List::empty() }
304    }
305
306    /// Returns `true` if this `Place` contains a `Deref` projection.
307    ///
308    /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
309    /// same region of memory as its base.
310    pub fn is_indirect(&self) -> bool {
311        self.projection.iter().any(|elem| elem.is_indirect())
312    }
313
314    /// Returns `true` if this `Place`'s first projection is `Deref`.
315    ///
316    /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
317    /// `Deref` projections can only occur as the first projection. In that case this method
318    /// is equivalent to `is_indirect`, but faster.
319    pub fn is_indirect_first_projection(&self) -> bool {
320        self.as_ref().is_indirect_first_projection()
321    }
322
323    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
324    /// a single deref of a local.
325    #[inline(always)]
326    pub fn local_or_deref_local(&self) -> Option<Local> {
327        self.as_ref().local_or_deref_local()
328    }
329
330    /// If this place represents a local variable like `_X` with no
331    /// projections, return `Some(_X)`.
332    #[inline(always)]
333    pub fn as_local(&self) -> Option<Local> {
334        self.as_ref().as_local()
335    }
336
337    #[inline]
338    pub fn as_ref(&self) -> PlaceRef<'tcx> {
339        PlaceRef { local: self.local, projection: self.projection }
340    }
341
342    /// Iterate over the projections in evaluation order, i.e., the first element is the base with
343    /// its projection and then subsequently more projections are added.
344    /// As a concrete example, given the place a.b.c, this would yield:
345    /// - (a, .b)
346    /// - (a.b, .c)
347    ///
348    /// Given a place without projections, the iterator is empty.
349    #[inline]
350    pub fn iter_projections(
351        self,
352    ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
353        self.as_ref().iter_projections()
354    }
355
356    /// Generates a new place by appending `more_projections` to the existing ones
357    /// and interning the result.
358    pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
359        if more_projections.is_empty() {
360            return self;
361        }
362
363        self.as_ref().project_deeper(more_projections, tcx)
364    }
365
366    pub fn ty_from<D: ?Sized>(
367        local: Local,
368        projection: &[PlaceElem<'tcx>],
369        local_decls: &D,
370        tcx: TyCtxt<'tcx>,
371    ) -> PlaceTy<'tcx>
372    where
373        D: HasLocalDecls<'tcx>,
374    {
375        PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection)
376    }
377
378    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
379    where
380        D: HasLocalDecls<'tcx>,
381    {
382        Place::ty_from(self.local, self.projection, local_decls, tcx)
383    }
384}
385
386impl From<Local> for Place<'_> {
387    #[inline]
388    fn from(local: Local) -> Self {
389        Place { local, projection: List::empty() }
390    }
391}
392
393impl<'tcx> PlaceRef<'tcx> {
394    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
395    /// a single deref of a local.
396    pub fn local_or_deref_local(&self) -> Option<Local> {
397        match *self {
398            PlaceRef { local, projection: [] }
399            | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
400            _ => None,
401        }
402    }
403
404    /// Returns `true` if this `Place` contains a `Deref` projection.
405    ///
406    /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
407    /// same region of memory as its base.
408    pub fn is_indirect(&self) -> bool {
409        self.projection.iter().any(|elem| elem.is_indirect())
410    }
411
412    /// Returns `true` if this `Place`'s first projection is `Deref`.
413    ///
414    /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
415    /// `Deref` projections can only occur as the first projection. In that case this method
416    /// is equivalent to `is_indirect`, but faster.
417    pub fn is_indirect_first_projection(&self) -> bool {
418        // To make sure this is not accidentally used in wrong mir phase
419        debug_assert!(
420            self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
421        );
422        self.projection.first() == Some(&PlaceElem::Deref)
423    }
424
425    /// If this place represents a local variable like `_X` with no
426    /// projections, return `Some(_X)`.
427    #[inline]
428    pub fn as_local(&self) -> Option<Local> {
429        match *self {
430            PlaceRef { local, projection: [] } => Some(local),
431            _ => None,
432        }
433    }
434
435    #[inline]
436    pub fn to_place(&self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
437        Place { local: self.local, projection: tcx.mk_place_elems(self.projection) }
438    }
439
440    #[inline]
441    pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
442        if let &[ref proj_base @ .., elem] = self.projection {
443            Some((PlaceRef { local: self.local, projection: proj_base }, elem))
444        } else {
445            None
446        }
447    }
448
449    /// Iterate over the projections in evaluation order, i.e., the first element is the base with
450    /// its projection and then subsequently more projections are added.
451    /// As a concrete example, given the place a.b.c, this would yield:
452    /// - (a, .b)
453    /// - (a.b, .c)
454    ///
455    /// Given a place without projections, the iterator is empty.
456    #[inline]
457    pub fn iter_projections(
458        self,
459    ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
460        self.projection.iter().enumerate().map(move |(i, proj)| {
461            let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
462            (base, *proj)
463        })
464    }
465
466    /// Generates a new place by appending `more_projections` to the existing ones
467    /// and interning the result.
468    pub fn project_deeper(
469        self,
470        more_projections: &[PlaceElem<'tcx>],
471        tcx: TyCtxt<'tcx>,
472    ) -> Place<'tcx> {
473        let mut v: Vec<PlaceElem<'tcx>>;
474
475        let new_projections = if self.projection.is_empty() {
476            more_projections
477        } else {
478            v = Vec::with_capacity(self.projection.len() + more_projections.len());
479            v.extend(self.projection);
480            v.extend(more_projections);
481            &v
482        };
483
484        Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
485    }
486
487    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
488    where
489        D: HasLocalDecls<'tcx>,
490    {
491        Place::ty_from(self.local, self.projection, local_decls, tcx)
492    }
493}
494
495impl From<Local> for PlaceRef<'_> {
496    #[inline]
497    fn from(local: Local) -> Self {
498        PlaceRef { local, projection: &[] }
499    }
500}
501
502///////////////////////////////////////////////////////////////////////////
503// Operands
504
505impl<'tcx> Operand<'tcx> {
506    /// Convenience helper to make a constant that refers to the fn
507    /// with given `DefId` and args. Since this is used to synthesize
508    /// MIR, assumes `user_ty` is None.
509    pub fn function_handle(
510        tcx: TyCtxt<'tcx>,
511        def_id: DefId,
512        args: impl IntoIterator<Item = GenericArg<'tcx>>,
513        span: Span,
514    ) -> Self {
515        let ty = Ty::new_fn_def(tcx, def_id, args);
516        Operand::Constant(Box::new(ConstOperand {
517            span,
518            user_ty: None,
519            const_: Const::Val(ConstValue::ZeroSized, ty),
520        }))
521    }
522
523    pub fn is_move(&self) -> bool {
524        matches!(self, Operand::Move(..))
525    }
526
527    /// Convenience helper to make a literal-like constant from a given scalar value.
528    /// Since this is used to synthesize MIR, assumes `user_ty` is None.
529    pub fn const_from_scalar(
530        tcx: TyCtxt<'tcx>,
531        ty: Ty<'tcx>,
532        val: Scalar,
533        span: Span,
534    ) -> Operand<'tcx> {
535        debug_assert!({
536            let typing_env = ty::TypingEnv::fully_monomorphized();
537            let type_size = tcx
538                .layout_of(typing_env.as_query_input(ty))
539                .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
540                .size;
541            let scalar_size = match val {
542                Scalar::Int(int) => int.size(),
543                _ => panic!("Invalid scalar type {val:?}"),
544            };
545            scalar_size == type_size
546        });
547        Operand::Constant(Box::new(ConstOperand {
548            span,
549            user_ty: None,
550            const_: Const::Val(ConstValue::Scalar(val), ty),
551        }))
552    }
553
554    pub fn to_copy(&self) -> Self {
555        match *self {
556            Operand::Copy(_) | Operand::Constant(_) => self.clone(),
557            Operand::Move(place) => Operand::Copy(place),
558        }
559    }
560
561    /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
562    /// constant.
563    pub fn place(&self) -> Option<Place<'tcx>> {
564        match self {
565            Operand::Copy(place) | Operand::Move(place) => Some(*place),
566            Operand::Constant(_) => None,
567        }
568    }
569
570    /// Returns the `ConstOperand` that is the target of this `Operand`, or `None` if this `Operand` is a
571    /// place.
572    pub fn constant(&self) -> Option<&ConstOperand<'tcx>> {
573        match self {
574            Operand::Constant(x) => Some(&**x),
575            Operand::Copy(_) | Operand::Move(_) => None,
576        }
577    }
578
579    /// Gets the `ty::FnDef` from an operand if it's a constant function item.
580    ///
581    /// While this is unlikely in general, it's the normal case of what you'll
582    /// find as the `func` in a [`TerminatorKind::Call`].
583    pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
584        let const_ty = self.constant()?.const_.ty();
585        if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
586    }
587
588    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
589    where
590        D: HasLocalDecls<'tcx>,
591    {
592        match self {
593            &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
594            Operand::Constant(c) => c.const_.ty(),
595        }
596    }
597
598    pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span
599    where
600        D: HasLocalDecls<'tcx>,
601    {
602        match self {
603            &Operand::Copy(ref l) | &Operand::Move(ref l) => {
604                local_decls.local_decls()[l.local].source_info.span
605            }
606            Operand::Constant(c) => c.span,
607        }
608    }
609}
610
611impl<'tcx> ConstOperand<'tcx> {
612    pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
613        match self.const_.try_to_scalar() {
614            Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance.alloc_id()) {
615                GlobalAlloc::Static(def_id) => {
616                    assert!(!tcx.is_thread_local_static(def_id));
617                    Some(def_id)
618                }
619                _ => None,
620            },
621            _ => None,
622        }
623    }
624
625    #[inline]
626    pub fn ty(&self) -> Ty<'tcx> {
627        self.const_.ty()
628    }
629}
630
631///////////////////////////////////////////////////////////////////////////
632/// Rvalues
633
634pub enum RvalueInitializationState {
635    Shallow,
636    Deep,
637}
638
639impl<'tcx> Rvalue<'tcx> {
640    /// Returns true if rvalue can be safely removed when the result is unused.
641    #[inline]
642    pub fn is_safe_to_remove(&self) -> bool {
643        match self {
644            // Pointer to int casts may be side-effects due to exposing the provenance.
645            // While the model is undecided, we should be conservative. See
646            // <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
647            Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => false,
648
649            Rvalue::Use(_)
650            | Rvalue::CopyForDeref(_)
651            | Rvalue::Repeat(_, _)
652            | Rvalue::Ref(_, _, _)
653            | Rvalue::ThreadLocalRef(_)
654            | Rvalue::RawPtr(_, _)
655            | Rvalue::Len(_)
656            | Rvalue::Cast(
657                CastKind::IntToInt
658                | CastKind::FloatToInt
659                | CastKind::FloatToFloat
660                | CastKind::IntToFloat
661                | CastKind::FnPtrToPtr
662                | CastKind::PtrToPtr
663                | CastKind::PointerCoercion(_, _)
664                | CastKind::PointerWithExposedProvenance
665                | CastKind::Transmute,
666                _,
667                _,
668            )
669            | Rvalue::BinaryOp(_, _)
670            | Rvalue::NullaryOp(_, _)
671            | Rvalue::UnaryOp(_, _)
672            | Rvalue::Discriminant(_)
673            | Rvalue::Aggregate(_, _)
674            | Rvalue::ShallowInitBox(_, _)
675            | Rvalue::WrapUnsafeBinder(_, _) => true,
676        }
677    }
678
679    pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
680    where
681        D: HasLocalDecls<'tcx>,
682    {
683        match *self {
684            Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
685            Rvalue::Repeat(ref operand, count) => {
686                Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
687            }
688            Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
689            Rvalue::Ref(reg, bk, ref place) => {
690                let place_ty = place.ty(local_decls, tcx).ty;
691                Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy())
692            }
693            Rvalue::RawPtr(kind, ref place) => {
694                let place_ty = place.ty(local_decls, tcx).ty;
695                Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy())
696            }
697            Rvalue::Len(..) => tcx.types.usize,
698            Rvalue::Cast(.., ty) => ty,
699            Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
700                let lhs_ty = lhs.ty(local_decls, tcx);
701                let rhs_ty = rhs.ty(local_decls, tcx);
702                op.ty(tcx, lhs_ty, rhs_ty)
703            }
704            Rvalue::UnaryOp(op, ref operand) => {
705                let arg_ty = operand.ty(local_decls, tcx);
706                op.ty(tcx, arg_ty)
707            }
708            Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
709            Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
710                tcx.types.usize
711            }
712            Rvalue::NullaryOp(NullOp::ContractChecks, _)
713            | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
714            Rvalue::Aggregate(ref ak, ref ops) => match **ak {
715                AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
716                AggregateKind::Tuple => {
717                    Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
718                }
719                AggregateKind::Adt(did, _, args, _, _) => tcx.type_of(did).instantiate(tcx, args),
720                AggregateKind::Closure(did, args) => Ty::new_closure(tcx, did, args),
721                AggregateKind::Coroutine(did, args) => Ty::new_coroutine(tcx, did, args),
722                AggregateKind::CoroutineClosure(did, args) => {
723                    Ty::new_coroutine_closure(tcx, did, args)
724                }
725                AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability),
726            },
727            Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
728            Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
729            Rvalue::WrapUnsafeBinder(_, ty) => ty,
730        }
731    }
732
733    #[inline]
734    /// Returns `true` if this rvalue is deeply initialized (most rvalues) or
735    /// whether its only shallowly initialized (`Rvalue::Box`).
736    pub fn initialization_state(&self) -> RvalueInitializationState {
737        match *self {
738            Rvalue::ShallowInitBox(_, _) => RvalueInitializationState::Shallow,
739            _ => RvalueInitializationState::Deep,
740        }
741    }
742}
743
744impl BorrowKind {
745    pub fn mutability(&self) -> Mutability {
746        match *self {
747            BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not,
748            BorrowKind::Mut { .. } => Mutability::Mut,
749        }
750    }
751
752    /// Returns whether borrows represented by this kind are allowed to be split into separate
753    /// Reservation and Activation phases.
754    pub fn allows_two_phase_borrow(&self) -> bool {
755        match *self {
756            BorrowKind::Shared
757            | BorrowKind::Fake(_)
758            | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
759                false
760            }
761            BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
762        }
763    }
764
765    pub fn to_mutbl_lossy(self) -> hir::Mutability {
766        match self {
767            BorrowKind::Mut { .. } => hir::Mutability::Mut,
768            BorrowKind::Shared => hir::Mutability::Not,
769
770            // We have no type corresponding to a shallow borrow, so use
771            // `&` as an approximation.
772            BorrowKind::Fake(_) => hir::Mutability::Not,
773        }
774    }
775}
776
777impl<'tcx> NullOp<'tcx> {
778    pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
779        match self {
780            NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) => tcx.types.usize,
781            NullOp::UbChecks | NullOp::ContractChecks => tcx.types.bool,
782        }
783    }
784}
785
786impl<'tcx> UnOp {
787    pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
788        match self {
789            UnOp::Not | UnOp::Neg => arg_ty,
790            UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx),
791        }
792    }
793}
794
795impl<'tcx> BinOp {
796    pub fn ty(&self, tcx: TyCtxt<'tcx>, lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>) -> Ty<'tcx> {
797        // FIXME: handle SIMD correctly
798        match self {
799            &BinOp::Add
800            | &BinOp::AddUnchecked
801            | &BinOp::Sub
802            | &BinOp::SubUnchecked
803            | &BinOp::Mul
804            | &BinOp::MulUnchecked
805            | &BinOp::Div
806            | &BinOp::Rem
807            | &BinOp::BitXor
808            | &BinOp::BitAnd
809            | &BinOp::BitOr => {
810                // these should be integers or floats of the same size.
811                assert_eq!(lhs_ty, rhs_ty);
812                lhs_ty
813            }
814            &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => {
815                // these should be integers of the same size.
816                assert_eq!(lhs_ty, rhs_ty);
817                Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool])
818            }
819            &BinOp::Shl
820            | &BinOp::ShlUnchecked
821            | &BinOp::Shr
822            | &BinOp::ShrUnchecked
823            | &BinOp::Offset => {
824                lhs_ty // lhs_ty can be != rhs_ty
825            }
826            &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
827                tcx.types.bool
828            }
829            &BinOp::Cmp => {
830                // these should be integer-like types of the same size.
831                assert_eq!(lhs_ty, rhs_ty);
832                tcx.ty_ordering_enum(None)
833            }
834        }
835    }
836    pub(crate) fn to_hir_binop(self) -> hir::BinOpKind {
837        match self {
838            // HIR `+`/`-`/`*` can map to either of these MIR BinOp, depending
839            // on whether overflow checks are enabled or not.
840            BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add,
841            BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub,
842            BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul,
843            BinOp::Div => hir::BinOpKind::Div,
844            BinOp::Rem => hir::BinOpKind::Rem,
845            BinOp::BitXor => hir::BinOpKind::BitXor,
846            BinOp::BitAnd => hir::BinOpKind::BitAnd,
847            BinOp::BitOr => hir::BinOpKind::BitOr,
848            BinOp::Shl => hir::BinOpKind::Shl,
849            BinOp::Shr => hir::BinOpKind::Shr,
850            BinOp::Eq => hir::BinOpKind::Eq,
851            BinOp::Ne => hir::BinOpKind::Ne,
852            BinOp::Lt => hir::BinOpKind::Lt,
853            BinOp::Gt => hir::BinOpKind::Gt,
854            BinOp::Le => hir::BinOpKind::Le,
855            BinOp::Ge => hir::BinOpKind::Ge,
856            // We don't have HIR syntax for these.
857            BinOp::Cmp
858            | BinOp::AddUnchecked
859            | BinOp::SubUnchecked
860            | BinOp::MulUnchecked
861            | BinOp::ShlUnchecked
862            | BinOp::ShrUnchecked
863            | BinOp::Offset => {
864                unreachable!()
865            }
866        }
867    }
868
869    /// If this is a `FooWithOverflow`, return `Some(Foo)`.
870    pub fn overflowing_to_wrapping(self) -> Option<BinOp> {
871        Some(match self {
872            BinOp::AddWithOverflow => BinOp::Add,
873            BinOp::SubWithOverflow => BinOp::Sub,
874            BinOp::MulWithOverflow => BinOp::Mul,
875            _ => return None,
876        })
877    }
878
879    /// Returns whether this is a `FooWithOverflow`
880    pub fn is_overflowing(self) -> bool {
881        self.overflowing_to_wrapping().is_some()
882    }
883
884    /// If this is a `Foo`, return `Some(FooWithOverflow)`.
885    pub fn wrapping_to_overflowing(self) -> Option<BinOp> {
886        Some(match self {
887            BinOp::Add => BinOp::AddWithOverflow,
888            BinOp::Sub => BinOp::SubWithOverflow,
889            BinOp::Mul => BinOp::MulWithOverflow,
890            _ => return None,
891        })
892    }
893}
894
895impl From<Mutability> for RawPtrKind {
896    fn from(other: Mutability) -> Self {
897        match other {
898            Mutability::Mut => RawPtrKind::Mut,
899            Mutability::Not => RawPtrKind::Const,
900        }
901    }
902}
903
904impl RawPtrKind {
905    pub fn is_fake(self) -> bool {
906        match self {
907            RawPtrKind::Mut | RawPtrKind::Const => false,
908            RawPtrKind::FakeForPtrMetadata => true,
909        }
910    }
911
912    pub fn to_mutbl_lossy(self) -> Mutability {
913        match self {
914            RawPtrKind::Mut => Mutability::Mut,
915            RawPtrKind::Const => Mutability::Not,
916
917            // We have no type corresponding to a fake borrow, so use
918            // `*const` as an approximation.
919            RawPtrKind::FakeForPtrMetadata => Mutability::Not,
920        }
921    }
922
923    pub fn ptr_str(self) -> &'static str {
924        match self {
925            RawPtrKind::Mut => "mut",
926            RawPtrKind::Const => "const",
927            RawPtrKind::FakeForPtrMetadata => "const (fake)",
928        }
929    }
930}