rustc_middle/mir/
statement.rs

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