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