Skip to main content

hir_ty/
mir.rs

1//! MIR definitions and implementation
2
3use std::{fmt::Display, iter};
4
5use hir_def::{
6    FieldId, LocalFieldId, StaticId, UnionId, VariantId,
7    hir::{BindingId, Expr, ExprId, Ordering, PatId},
8};
9use intern::{InternedSlice, InternedSliceRef, impl_slice_internable};
10use la_arena::{Arena, ArenaMap, Idx, RawIdx};
11use macros::{TypeFoldable, TypeVisitable};
12use rustc_ast_ir::Mutability;
13use rustc_hash::FxHashMap;
14use rustc_type_ir::{
15    CollectAndApply, GenericTypeVisitable,
16    inherent::{GenericArgs as _, IntoKind, Ty as _},
17};
18use salsa::Update;
19use smallvec::{SmallVec, smallvec};
20use stdx::impl_from;
21
22use crate::{
23    CallableDefId, InferBodyId, InferenceResult, MemoryMap,
24    db::{HirDatabase, InternedClosureId},
25    infer::PointerCast,
26    next_solver::{
27        Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv,
28        StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind,
29        impl_stored_interned_slice,
30        infer::{InferCtxt, traits::ObligationCause},
31        obligation_ctxt::ObligationCtxt,
32    },
33};
34
35mod borrowck;
36mod eval;
37mod lower;
38mod monomorphization;
39mod pretty;
40
41pub use borrowck::{BorrowckResult, MutabilityReason};
42pub use eval::{
43    Evaluator, MirEvalError, VTableMap, interpret_mir, pad16, render_const_using_debug_impl,
44};
45pub use lower::{
46    MirLowerError, lower_body_to_mir, lower_to_mir_with_store, mir_body_for_closure_query,
47    mir_body_query,
48};
49pub use monomorphization::{
50    monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query,
51};
52
53pub type BasicBlockId = Idx<BasicBlock>;
54pub type LocalId = Idx<Local>;
55
56fn return_slot() -> LocalId {
57    LocalId::from_raw(RawIdx::from(0))
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Hash)]
61pub struct Local {
62    pub ty: StoredTy,
63}
64
65/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
66/// the memory model. One proposal for a definition of values can be found [on UCG][value-def].
67///
68/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md
69///
70/// The most common way to create values is via loading a place. Loading a place is an operation
71/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed*
72/// operation. The nature of the value produced depends on the type of the conversion. Furthermore,
73/// there may be other effects: if the type has a validity constraint loading the place might be UB
74/// if the validity constraint is not met.
75///
76/// **Needs clarification:** Ralf proposes that loading a place not have side-effects.
77/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
78/// something we can even decide without knowing more about Rust's memory model?
79///
80/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri
81/// currently implements it, but it seems like this may be something to check against in the
82/// validator.
83#[derive(Debug, PartialEq, Eq, Clone)]
84pub struct Operand {
85    kind: OperandKind,
86    // FIXME : This should actually just be of type `MirSpan`.
87    span: Option<MirSpan>,
88}
89
90#[derive(Debug, PartialEq, Eq, Clone)]
91pub enum OperandKind {
92    /// Creates a value by loading the given place.
93    ///
94    /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
95    /// is no such requirement.
96    Copy(Place),
97
98    /// Creates a value by performing loading the place, just like the `Copy` operand.
99    ///
100    /// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide
101    /// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this
102    /// place without first re-initializing it.
103    ///
104    /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
105    Move(Place),
106    /// Constants are already semantically values, and remain unchanged.
107    Constant {
108        konst: StoredConst,
109        ty: StoredTy,
110    },
111    Allocation {
112        allocation: StoredAllocation,
113    },
114    /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
115    /// handles it with the `Constant` variant somehow.
116    Static(StaticId),
117}
118
119impl<'db> Operand {
120    fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self {
121        Operand {
122            kind: OperandKind::Allocation {
123                allocation: Allocation::new(AllocationData { ty, memory: data, memory_map })
124                    .store(),
125            },
126            span: None,
127        }
128    }
129
130    fn from_bytes(data: Box<[u8]>, ty: Ty<'db>) -> Self {
131        Operand::from_concrete_const(data, MemoryMap::default(), ty)
132    }
133
134    fn const_zst(ty: Ty<'db>) -> Operand {
135        Self::from_bytes(Box::default(), ty)
136    }
137
138    fn from_fn(
139        db: &'db dyn HirDatabase,
140        func_id: hir_def::FunctionId,
141        generic_args: GenericArgs<'db>,
142    ) -> Operand {
143        let interner = DbInterner::new_no_crate(db);
144        let ty = Ty::new_fn_def(interner, CallableDefId::FunctionId(func_id).into(), generic_args);
145        Operand::from_bytes(Box::default(), ty)
146    }
147}
148
149/// The index of a field (whether of a struct/enum variant, tuple, or closure).
150/// For a struct/enum it converts from and to the LocalFieldId, for a tuple or closure it's simply the index.
151#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::Update, PartialOrd, Ord, Debug)]
152pub struct FieldIndex(pub u32);
153
154impl FieldIndex {
155    pub fn to_local_field_id(self) -> LocalFieldId {
156        LocalFieldId::from_raw(RawIdx::from_u32(self.0))
157    }
158}
159
160impl From<LocalFieldId> for FieldIndex {
161    fn from(value: LocalFieldId) -> Self {
162        FieldIndex(value.into_raw().into_u32())
163    }
164}
165
166#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
167pub enum ProjectionElem<V: PartialEq> {
168    Deref,
169    /// A field (e.g., `f` in `_1.f`).
170    Field(FieldIndex),
171    /// Index into a slice/array.
172    Index(V),
173    /// These indices are generated by slice patterns.
174    ConstantIndex {
175        offset: u64,
176        from_end: bool,
177    },
178    /// These indices are generated by slice patterns.
179    Subslice {
180        from: u64,
181        to: u64,
182    },
183    /// "Downcast" to a variant of an enum or a coroutine.
184    Downcast(VariantId),
185}
186
187impl<V: PartialEq> ProjectionElem<V> {
188    pub fn map<V2: PartialEq>(self, v: impl FnOnce(V) -> V2) -> ProjectionElem<V2> {
189        match self {
190            ProjectionElem::Deref => ProjectionElem::Deref,
191            ProjectionElem::Field(field_index) => ProjectionElem::Field(field_index),
192            ProjectionElem::Index(idx) => ProjectionElem::Index(v(idx)),
193            ProjectionElem::ConstantIndex { offset, from_end } => {
194                ProjectionElem::ConstantIndex { offset, from_end }
195            }
196            ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
197            ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id),
198        }
199    }
200
201    pub fn try_map<V2: PartialEq>(
202        self,
203        v: impl FnOnce(V) -> Option<V2>,
204    ) -> Option<ProjectionElem<V2>> {
205        Some(match self {
206            ProjectionElem::Deref => ProjectionElem::Deref,
207            ProjectionElem::Field(field_index) => ProjectionElem::Field(field_index),
208            ProjectionElem::Index(idx) => ProjectionElem::Index(v(idx)?),
209            ProjectionElem::ConstantIndex { offset, from_end } => {
210                ProjectionElem::ConstantIndex { offset, from_end }
211            }
212            ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
213            ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id),
214        })
215    }
216}
217
218type PlaceElem = ProjectionElem<LocalId>;
219
220impl<W: crate::next_solver::WorldExposer> GenericTypeVisitable<W> for PlaceElem {
221    fn generic_visit_with(&self, _: &mut W) {}
222}
223
224impl_slice_internable!(gc; ProjectionStorage, (), PlaceElem);
225impl_stored_interned_slice!(ProjectionStorage, Projection, StoredProjection);
226
227#[derive(Clone, Copy, PartialEq, Eq, Hash)]
228pub struct Projection<'db> {
229    interned: InternedSliceRef<'db, ProjectionStorage>,
230}
231
232impl<'db> std::fmt::Debug for Projection<'db> {
233    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        (*self).as_slice().fmt(fmt)
235    }
236}
237
238impl<'db> Projection<'db> {
239    pub fn new_from_iter<I, T>(args: I) -> T::Output
240    where
241        I: IntoIterator<Item = T>,
242        T: CollectAndApply<PlaceElem, Self>,
243    {
244        CollectAndApply::collect_and_apply(args.into_iter(), Self::new_from_slice)
245    }
246
247    #[inline]
248    pub fn new_from_slice(slice: &[PlaceElem]) -> Self {
249        Self { interned: InternedSlice::from_header_and_slice((), slice) }
250    }
251
252    #[inline]
253    pub fn as_slice(self) -> &'db [PlaceElem] {
254        &self.interned.get().slice
255    }
256
257    pub fn project(self, projection: PlaceElem) -> Projection<'db> {
258        Projection::new_from_iter(self.as_slice().iter().copied().chain([projection]))
259    }
260}
261
262impl<'db> std::ops::Deref for Projection<'db> {
263    type Target = [PlaceElem];
264
265    fn deref(&self) -> &Self::Target {
266        self.as_slice()
267    }
268}
269
270impl StoredProjection {
271    // FIXME: rename to as_slice
272    pub fn lookup(&self) -> &[PlaceElem] {
273        self.as_ref().as_slice()
274    }
275
276    pub fn is_empty(&self) -> bool {
277        self.lookup().is_empty()
278    }
279}
280
281// FIXME: would be nicer to rename PlaceRef -> Place, Place -> StoredPlace, but I didn't want to blow up the diff
282#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
283pub struct PlaceRef<'db> {
284    pub local: LocalId,
285    pub projection: Projection<'db>,
286}
287
288#[derive(Debug, Clone, PartialEq, Eq, Hash)]
289pub struct Place {
290    pub local: LocalId,
291    pub projection: StoredProjection,
292}
293
294impl Place {
295    pub fn as_ref<'db>(&self) -> PlaceRef<'db> {
296        PlaceRef { local: self.local, projection: self.projection.as_ref() }
297    }
298}
299
300impl<'db> PlaceRef<'db> {
301    fn is_parent(&self, child: PlaceRef<'db>) -> bool {
302        self.local == child.local
303            && child.projection.as_slice().starts_with(self.projection.as_slice())
304    }
305
306    /// The place itself is not included
307    fn iterate_over_parents<'a>(&'a self) -> impl Iterator<Item = PlaceRef<'db>> + 'a {
308        let projection = self.projection.as_slice();
309        (0..projection.len()).map(move |x| PlaceRef {
310            local: self.local,
311            projection: Projection::new_from_slice(&projection[0..x]),
312        })
313    }
314
315    fn project(&self, projection: PlaceElem) -> PlaceRef<'db> {
316        PlaceRef { local: self.local, projection: self.projection.project(projection) }
317    }
318
319    pub fn store(&self) -> Place {
320        Place { local: self.local, projection: self.projection.store() }
321    }
322    pub fn ty(
323        &self,
324        body: &MirBody<'db>,
325        infcx: &InferCtxt<'db>,
326        env: ParamEnv<'db>,
327    ) -> PlaceTy<'db> {
328        PlaceTy::from_ty(body.locals[self.local].ty.as_ref()).multi_projection_ty(
329            infcx,
330            env,
331            self.projection.as_slice(),
332        )
333    }
334}
335
336impl<'db> From<LocalId> for PlaceRef<'db> {
337    fn from(local: LocalId) -> Self {
338        let empty: &[PlaceElem] = &[];
339        PlaceRef { local, projection: Projection::new_from_slice(empty) }
340    }
341}
342
343#[derive(Debug, PartialEq, Eq, Clone)]
344pub enum AggregateKind {
345    /// The type is of the element
346    Array(StoredTy),
347    /// The type is of the tuple
348    Tuple(StoredTy),
349    Adt(VariantId, StoredGenericArgs),
350    Union(UnionId, FieldId),
351    Closure(StoredTy),
352    //Coroutine(LocalDefId, SubstsRef, Movability),
353}
354
355#[derive(Debug, Clone, Hash, PartialEq, Eq)]
356pub struct SwitchTargets {
357    /// Possible values. The locations to branch to in each case
358    /// are found in the corresponding indices from the `targets` vector.
359    values: SmallVec<[u128; 1]>,
360
361    /// Possible branch sites. The last element of this vector is used
362    /// for the otherwise branch, so targets.len() == values.len() + 1
363    /// should hold.
364    //
365    // This invariant is quite non-obvious and also could be improved.
366    // One way to make this invariant is to have something like this instead:
367    //
368    // branches: Vec<(ConstInt, BasicBlock)>,
369    // otherwise: Option<BasicBlock> // exhaustive if None
370    //
371    // However we’ve decided to keep this as-is until we figure a case
372    // where some other approach seems to be strictly better than other.
373    targets: SmallVec<[BasicBlockId; 2]>,
374}
375
376impl SwitchTargets {
377    /// Creates switch targets from an iterator of values and target blocks.
378    ///
379    /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
380    /// `goto otherwise;`.
381    pub fn new(
382        targets: impl Iterator<Item = (u128, BasicBlockId)>,
383        otherwise: BasicBlockId,
384    ) -> Self {
385        let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
386        targets.push(otherwise);
387        Self { values, targets }
388    }
389
390    /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
391    /// and to `else_` if not.
392    pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
393        Self { values: smallvec![value], targets: smallvec![then, else_] }
394    }
395
396    /// Returns the fallback target that is jumped to when none of the values match the operand.
397    pub fn otherwise(&self) -> BasicBlockId {
398        *self.targets.last().unwrap()
399    }
400
401    /// Returns an iterator over the switch targets.
402    ///
403    /// The iterator will yield tuples containing the value and corresponding target to jump to, not
404    /// including the `otherwise` fallback target.
405    ///
406    /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
407    pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
408        iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
409    }
410
411    /// Returns a slice with all possible jump targets (including the fallback target).
412    pub fn all_targets(&self) -> &[BasicBlockId] {
413        &self.targets
414    }
415
416    /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
417    /// specific value. This cannot fail, as it'll return the `otherwise`
418    /// branch if there's not a specific match for the value.
419    pub fn target_for_value(&self, value: u128) -> BasicBlockId {
420        self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
421    }
422}
423
424#[derive(Debug, PartialEq, Eq, Clone)]
425pub struct Terminator {
426    pub span: MirSpan,
427    pub kind: TerminatorKind,
428}
429
430#[derive(Debug, PartialEq, Eq, Clone)]
431pub enum TerminatorKind {
432    /// Block has one successor; we continue execution there.
433    Goto { target: BasicBlockId },
434
435    /// Switches based on the computed value.
436    ///
437    /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
438    /// integer, char, or bool, and must match the given type. Then, if the list of switch targets
439    /// contains the computed value, continues execution at the associated basic block. Otherwise,
440    /// continues execution at the "otherwise" basic block.
441    ///
442    /// Target values may not appear more than once.
443    SwitchInt {
444        /// The discriminant value being tested.
445        discr: Operand,
446
447        targets: SwitchTargets,
448    },
449
450    /// Indicates that the landing pad is finished and that the process should continue unwinding.
451    ///
452    /// Like a return, this marks the end of this invocation of the function.
453    ///
454    /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
455    /// deaggregation runs.
456    UnwindResume,
457
458    /// Indicates that the landing pad is finished and that the process should abort.
459    ///
460    /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
461    /// cleanup blocks.
462    Abort,
463
464    /// Returns from the function.
465    ///
466    /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
467    /// likely at least assigns the value currently in the return place (`_0`) to the place
468    /// specified in the associated `Call` terminator in the calling function, as if assigned via
469    /// `dest = move _0`. It might additionally do other things, like have side-effects in the
470    /// aliasing model.
471    ///
472    /// If the body is a coroutine body, this has slightly different semantics; it instead causes a
473    /// `CoroutineState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
474    /// to the return place.
475    Return,
476
477    /// Indicates a terminator that can never be reached.
478    ///
479    /// Executing this terminator is UB.
480    Unreachable,
481
482    /// The behavior of this statement differs significantly before and after drop elaboration.
483    /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
484    /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
485    /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
486    /// issue tracking if drop glue has any interesting semantics in addition to those of a function
487    /// call?)
488    ///
489    /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
490    /// `Drop` will be executed if...
491    ///
492    /// **Needs clarification**: End of that sentence. This in effect should document the exact
493    /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
494    ///
495    /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
496    /// > the place or one of its "parents" occurred more recently than a move out of it. This does not
497    /// > consider indirect assignments.
498    Drop { place: Place, target: BasicBlockId, unwind: Option<BasicBlockId> },
499
500    /// Drops the place and assigns a new value to it.
501    ///
502    /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
503    /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
504    /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
505    /// explained by the elaboration:
506    ///
507    /// ```ignore (MIR)
508    /// BB0 {
509    ///   DropAndReplace(P <- V, goto BB1, unwind BB2)
510    /// }
511    /// ```
512    ///
513    /// becomes
514    ///
515    /// ```ignore (MIR)
516    /// BB0 {
517    ///   Drop(P, goto BB1, unwind BB2)
518    /// }
519    /// BB1 {
520    ///   // P is now uninitialized
521    ///   P <- V
522    /// }
523    /// BB2 {
524    ///   // P is now uninitialized -- its dtor panicked
525    ///   P <- V
526    /// }
527    /// ```
528    ///
529    /// Disallowed after drop elaboration.
530    DropAndReplace {
531        place: Place,
532        value: Operand,
533        target: BasicBlockId,
534        unwind: Option<BasicBlockId>,
535    },
536
537    /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
538    /// the referred to function. The operand types must match the argument types of the function.
539    /// The return place type must match the return type. The type of the `func` operand must be
540    /// callable, meaning either a function pointer, a function type, or a closure type.
541    ///
542    /// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
543    /// operands not aliasing the return place. It is unclear how this is justified in MIR, see
544    /// [#71117].
545    ///
546    /// [#71117]: https://github.com/rust-lang/rust/issues/71117
547    Call {
548        /// The function that’s being called.
549        func: Operand,
550        /// Arguments the function is called with.
551        /// These are owned by the callee, which is free to modify them.
552        /// This allows the memory occupied by "by-value" arguments to be
553        /// reused across function calls without duplicating the contents.
554        args: Box<[Operand]>,
555        /// Where the returned value will be written
556        destination: Place,
557        /// Where to go after this call returns. If none, the call necessarily diverges.
558        target: Option<BasicBlockId>,
559        /// Cleanups to be done if the call unwinds.
560        cleanup: Option<BasicBlockId>,
561        /// `true` if this is from a call in HIR rather than from an overloaded
562        /// operator. True for overloaded function call.
563        from_hir_call: bool,
564        // This `Span` is the span of the function, without the dot and receiver
565        // (e.g. `foo(a, b)` in `x.foo(a, b)`
566        //fn_span: Span,
567    },
568
569    /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
570    /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
571    /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
572    /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
573    /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
574    /// assertion does not fail, execution continues at the specified basic block.
575    Assert {
576        cond: Operand,
577        expected: bool,
578        //msg: AssertMessage,
579        target: BasicBlockId,
580        cleanup: Option<BasicBlockId>,
581    },
582
583    /// Marks a suspend point.
584    ///
585    /// Like `Return` terminators in coroutine bodies, this computes `value` and then a
586    /// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
587    /// the return place of the function calling this one, and execution continues in the calling
588    /// function. When next invoked with the same first argument, execution of this function
589    /// continues at the `resume` basic block, with the second argument written to the `resume_arg`
590    /// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
591    ///
592    /// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
593    ///
594    /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
595    Yield {
596        /// The value to return.
597        value: Operand,
598        /// Where to resume to.
599        resume: BasicBlockId,
600        /// The place to store the resume argument in.
601        resume_arg: Place,
602        /// Cleanup to be done if the coroutine is dropped at this suspend point.
603        drop: Option<BasicBlockId>,
604    },
605
606    /// Indicates the end of dropping a coroutine.
607    ///
608    /// Semantically just a `return` (from the coroutines drop glue). Only permitted in the same situations
609    /// as `yield`.
610    ///
611    /// **Needs clarification**: Is that even correct? The coroutine drop code is always confusing
612    /// to me, because it's not even really in the current body.
613    ///
614    /// **Needs clarification**: Are there type system constraints on these terminators? Should
615    /// there be a "block type" like `cleanup` blocks for them?
616    CoroutineDrop,
617
618    /// A block where control flow only ever takes one real path, but borrowck needs to be more
619    /// conservative.
620    ///
621    /// At runtime this is semantically just a goto.
622    ///
623    /// Disallowed after drop elaboration.
624    FalseEdge {
625        /// The target normal control flow will take.
626        real_target: BasicBlockId,
627        /// A block control flow could conceptually jump to, but won't in
628        /// practice.
629        imaginary_target: BasicBlockId,
630    },
631
632    /// A terminator for blocks that only take one path in reality, but where we reserve the right
633    /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
634    /// with no function calls for example.
635    ///
636    /// At runtime this is semantically just a goto.
637    ///
638    /// Disallowed after drop elaboration.
639    FalseUnwind {
640        /// The target normal control flow will take.
641        real_target: BasicBlockId,
642        /// The imaginary cleanup block link. This particular path will never be taken
643        /// in practice, but in order to avoid fragility we want to always
644        /// consider it in borrowck. We don't want to accept programs which
645        /// pass borrowck only when `panic=abort` or some assertions are disabled
646        /// due to release vs. debug mode builds. This needs to be an `Option` because
647        /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
648        unwind: Option<BasicBlockId>,
649    },
650}
651
652// Order of variants in this enum matter: they are used to compare borrow kinds.
653#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
654pub enum BorrowKind {
655    /// Data must be immutable and is aliasable.
656    Shared,
657
658    /// The immediately borrowed place must be immutable, but projections from
659    /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
660    /// conflict with a mutable borrow of `a.b.c`.
661    ///
662    /// This is used when lowering matches: when matching on a place we want to
663    /// ensure that place have the same value from the start of the match until
664    /// an arm is selected. This prevents this code from compiling:
665    /// ```compile_fail,E0510
666    /// let mut x = &Some(0);
667    /// match *x {
668    ///     None => (),
669    ///     Some(_) if { x = &None; false } => (),
670    ///     Some(_) => (),
671    /// }
672    /// ```
673    /// This can't be a shared borrow because mutably borrowing (*x as Some).0
674    /// should not prevent `if let None = x { ... }`, for example, because the
675    /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
676    /// We can also report errors with this kind of borrow differently.
677    Shallow,
678
679    /// Data is mutable and not aliasable.
680    Mut { kind: MutBorrowKind },
681}
682
683// Order of variants in this enum matter: they are used to compare borrow kinds.
684#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
685pub enum MutBorrowKind {
686    /// Data must be immutable but not aliasable. This kind of borrow cannot currently
687    /// be expressed by the user and is used only in implicit closure bindings.
688    ClosureCapture,
689    Default,
690    /// This borrow arose from method-call auto-ref
691    /// (i.e., adjustment::Adjust::Borrow).
692    TwoPhasedBorrow,
693}
694
695impl BorrowKind {
696    fn from_hir_mutability(m: hir_def::type_ref::Mutability) -> Self {
697        match m {
698            hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
699            hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
700        }
701    }
702
703    fn from_rustc_mutability(m: rustc_ast_ir::Mutability) -> Self {
704        match m {
705            rustc_ast_ir::Mutability::Not => BorrowKind::Shared,
706            rustc_ast_ir::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
707        }
708    }
709
710    fn from_hir(bk: crate::infer::closure::analysis::BorrowKind) -> Self {
711        match bk {
712            crate::closure_analysis::BorrowKind::Immutable => Self::Shared,
713            crate::closure_analysis::BorrowKind::UniqueImmutable => {
714                Self::Mut { kind: MutBorrowKind::ClosureCapture }
715            }
716            crate::closure_analysis::BorrowKind::Mutable => {
717                Self::Mut { kind: MutBorrowKind::Default }
718            }
719        }
720    }
721}
722
723#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
724pub enum UnOp {
725    /// The `!` operator for logical inversion
726    Not,
727    /// The `-` operator for negation
728    Neg,
729}
730
731#[derive(Debug, PartialEq, Eq, Clone)]
732pub enum BinOp {
733    /// The `+` operator (addition)
734    Add,
735    /// The `-` operator (subtraction)
736    Sub,
737    /// The `*` operator (multiplication)
738    Mul,
739    /// The `/` operator (division)
740    ///
741    /// Division by zero is UB, because the compiler should have inserted checks
742    /// prior to this.
743    Div,
744    /// The `%` operator (modulus)
745    ///
746    /// Using zero as the modulus (second operand) is UB, because the compiler
747    /// should have inserted checks prior to this.
748    Rem,
749    /// The `^` operator (bitwise xor)
750    BitXor,
751    /// The `&` operator (bitwise and)
752    BitAnd,
753    /// The `|` operator (bitwise or)
754    BitOr,
755    /// The `<<` operator (shift left)
756    ///
757    /// The offset is truncated to the size of the first operand before shifting.
758    Shl,
759    /// The `>>` operator (shift right)
760    ///
761    /// The offset is truncated to the size of the first operand before shifting.
762    Shr,
763    /// The `==` operator (equality)
764    Eq,
765    /// The `<` operator (less than)
766    Lt,
767    /// The `<=` operator (less than or equal to)
768    Le,
769    /// The `!=` operator (not equal to)
770    Ne,
771    /// The `>=` operator (greater than or equal to)
772    Ge,
773    /// The `>` operator (greater than)
774    Gt,
775    /// The `ptr.offset` operator
776    Offset,
777}
778
779impl BinOp {
780    fn run_compare<T: PartialEq + PartialOrd>(&self, l: T, r: T) -> bool {
781        match self {
782            BinOp::Ge => l >= r,
783            BinOp::Gt => l > r,
784            BinOp::Le => l <= r,
785            BinOp::Lt => l < r,
786            BinOp::Eq => l == r,
787            BinOp::Ne => l != r,
788            x => panic!("`run_compare` called on operator {x:?}"),
789        }
790    }
791}
792
793impl Display for BinOp {
794    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
795        f.write_str(match self {
796            BinOp::Add => "+",
797            BinOp::Sub => "-",
798            BinOp::Mul => "*",
799            BinOp::Div => "/",
800            BinOp::Rem => "%",
801            BinOp::BitXor => "^",
802            BinOp::BitAnd => "&",
803            BinOp::BitOr => "|",
804            BinOp::Shl => "<<",
805            BinOp::Shr => ">>",
806            BinOp::Eq => "==",
807            BinOp::Lt => "<",
808            BinOp::Le => "<=",
809            BinOp::Ne => "!=",
810            BinOp::Ge => ">=",
811            BinOp::Gt => ">",
812            BinOp::Offset => "`offset`",
813        })
814    }
815}
816
817impl From<hir_def::hir::ArithOp> for BinOp {
818    fn from(value: hir_def::hir::ArithOp) -> Self {
819        match value {
820            hir_def::hir::ArithOp::Add => BinOp::Add,
821            hir_def::hir::ArithOp::Mul => BinOp::Mul,
822            hir_def::hir::ArithOp::Sub => BinOp::Sub,
823            hir_def::hir::ArithOp::Div => BinOp::Div,
824            hir_def::hir::ArithOp::Rem => BinOp::Rem,
825            hir_def::hir::ArithOp::Shl => BinOp::Shl,
826            hir_def::hir::ArithOp::Shr => BinOp::Shr,
827            hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
828            hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
829            hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
830        }
831    }
832}
833
834impl From<hir_def::hir::CmpOp> for BinOp {
835    fn from(value: hir_def::hir::CmpOp) -> Self {
836        match value {
837            hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
838            hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
839            hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
840            hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
841            hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
842            hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
843        }
844    }
845}
846
847impl From<Operand> for Rvalue {
848    fn from(x: Operand) -> Self {
849        Self::Use(x)
850    }
851}
852
853#[derive(Debug, PartialEq, Eq, Clone)]
854pub enum CastKind {
855    /// An exposing pointer to address cast. A cast between a pointer and an integer type, or
856    /// between a function pointer and an integer type.
857    /// See the docs on `expose_addr` for more details.
858    PointerExposeAddress,
859    /// An address-to-pointer cast that picks up an exposed provenance.
860    /// See the docs on `from_exposed_addr` for more details.
861    PointerFromExposedAddress,
862    /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
863    /// translated into `&raw mut/const *r`, i.e., they are not actually casts.
864    PtrToPtr,
865    /// Pointer related casts that are done by coercions.
866    PointerCoercion(PointerCast),
867    /// Cast into a dyn* object.
868    DynStar,
869    IntToInt,
870    FloatToInt,
871    FloatToFloat,
872    IntToFloat,
873    FnPtrToPtr,
874}
875
876#[derive(Debug, PartialEq, Eq, Clone)]
877pub enum Rvalue {
878    /// Yields the operand unchanged
879    Use(Operand),
880
881    /// Creates an array where each element is the value of the operand.
882    ///
883    /// Corresponds to source code like `[x; 32]`.
884    Repeat(Operand, StoredConst),
885
886    /// Creates a reference of the indicated kind to the place.
887    ///
888    /// There is not much to document here, because besides the obvious parts the semantics of this
889    /// are essentially entirely a part of the aliasing model. There are many UCG issues discussing
890    /// exactly what the behavior of this operation should be.
891    ///
892    /// `Shallow` borrows are disallowed after drop lowering.
893    Ref(BorrowKind, Place),
894
895    /// Creates a pointer/reference to the given thread local.
896    ///
897    /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
898    /// `*const T`, and if neither of those apply a `&T`.
899    ///
900    /// **Note:** This is a runtime operation that actually executes code and is in this sense more
901    /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
902    /// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
903    ///
904    /// **Needs clarification**: Are there weird additional semantics here related to the runtime
905    /// nature of this operation?
906    // ThreadLocalRef(DefId),
907    ThreadLocalRef(std::convert::Infallible),
908
909    /// Creates a pointer with the indicated mutability to the place.
910    ///
911    /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
912    /// `&raw v` or `addr_of!(v)`.
913    ///
914    /// Like with references, the semantics of this operation are heavily dependent on the aliasing
915    /// model.
916    // AddressOf(Mutability, Place),
917    AddressOf(std::convert::Infallible),
918
919    /// Yields the length of the place, as a `usize`.
920    ///
921    /// If the type of the place is an array, this is the array length. For slices (`[T]`, not
922    /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
923    /// ill-formed for places of other types.
924    Len(Place),
925
926    /// Performs essentially all of the casts that can be performed via `as`.
927    ///
928    /// This allows for casts from/to a variety of types.
929    ///
930    /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why
931    /// `ArrayToPointer` and `MutToConstPointer` are special.
932    Cast(CastKind, Operand, StoredTy),
933
934    // FIXME link to `pointer::offset` when it hits stable.
935    /// * `Offset` has the same semantics as `pointer::offset`, except that the second
936    ///   parameter may be a `usize` as well.
937    /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
938    ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be
939    ///   matching, up to the usual caveat of the lifetimes in function pointers.
940    /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
941    ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is
942    ///   truncated as needed.
943    /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
944    ///   types and return a value of that type.
945    /// * The remaining operations accept signed integers, unsigned integers, or floats with
946    ///   matching types and return a value of that type.
947    //BinaryOp(BinOp, Box<(Operand, Operand)>),
948    BinaryOp(std::convert::Infallible),
949
950    /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
951    ///
952    /// When overflow checking is disabled and we are generating run-time code, the error condition
953    /// is false. Otherwise, and always during CTFE, the error condition is determined as described
954    /// below.
955    ///
956    /// For addition, subtraction, and multiplication on integers the error condition is set when
957    /// the infinite precision result would be unequal to the actual result.
958    ///
959    /// For shift operations on integers the error condition is set when the value of right-hand
960    /// side is greater than or equal to the number of bits in the type of the left-hand side, or
961    /// when the value of right-hand side is negative.
962    ///
963    /// Other combinations of types and operators are unsupported.
964    CheckedBinaryOp(BinOp, Operand, Operand),
965
966    /// Computes a value as described by the operation.
967    //NullaryOp(NullOp, Ty),
968    NullaryOp(std::convert::Infallible),
969
970    /// Exactly like `BinaryOp`, but less operands.
971    ///
972    /// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
973    /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
974    /// return a value with the same type as their operand.
975    UnaryOp(UnOp, Operand),
976
977    /// Computes the discriminant of the place, returning it as an integer of type
978    /// `discriminant_ty`. Returns zero for types without discriminant.
979    ///
980    /// The validity requirements for the underlying value are undecided for this rvalue, see
981    /// [#91095]. Note too that the value of the discriminant is not the same thing as the
982    /// variant index; use `discriminant_for_variant` to convert.
983    ///
984    /// [#91095]: https://github.com/rust-lang/rust/issues/91095
985    Discriminant(Place),
986
987    /// Creates an aggregate value, like a tuple or struct.
988    ///
989    /// This is needed because dataflow analysis needs to distinguish
990    /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
991    /// has a destructor.
992    ///
993    /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After
994    /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too.
995    Aggregate(AggregateKind, Box<[Operand]>),
996
997    /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
998    ///
999    /// This is different from a normal transmute because dataflow analysis will treat the box as
1000    /// initialized but its content as uninitialized. Like other pointer casts, this in general
1001    /// affects alias analysis.
1002    ShallowInitBox(Operand, StoredTy),
1003
1004    /// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
1005    ShallowInitBoxWithAlloc(StoredTy),
1006
1007    /// A CopyForDeref is equivalent to a read from a place at the
1008    /// codegen level, but is treated specially by drop elaboration. When such a read happens, it
1009    /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
1010    /// that the only use of the returned value is a deref operation, immediately
1011    /// followed by one or more projections. Drop elaboration treats this rvalue as if the
1012    /// read never happened and just projects further. This allows simplifying various MIR
1013    /// optimizations and codegen backends that previously had to handle deref operations anywhere
1014    /// in a place.
1015    CopyForDeref(Place),
1016}
1017
1018#[derive(Debug, PartialEq, Eq, Clone)]
1019pub enum StatementKind {
1020    Assign(Place, Rvalue),
1021    FakeRead(Place),
1022    //SetDiscriminant {
1023    //    place: Box<Place>,
1024    //    variant_index: VariantIdx,
1025    //},
1026    Deinit(Place),
1027    StorageLive(LocalId),
1028    StorageDead(LocalId),
1029    //Retag(RetagKind, Box<Place>),
1030    //AscribeUserType(Place, UserTypeProjection, Variance),
1031    //Intrinsic(Box<NonDivergingIntrinsic>),
1032    Nop,
1033}
1034impl StatementKind {
1035    fn with_span(self, span: MirSpan) -> Statement {
1036        Statement { kind: self, span }
1037    }
1038}
1039
1040#[derive(Debug, PartialEq, Eq, Clone)]
1041pub struct Statement {
1042    pub kind: StatementKind,
1043    pub span: MirSpan,
1044}
1045
1046#[derive(Debug, Default, Clone, PartialEq, Eq)]
1047pub struct BasicBlock {
1048    /// List of statements in this block.
1049    pub statements: Vec<Statement>,
1050
1051    /// Terminator for this block.
1052    ///
1053    /// N.B., this should generally ONLY be `None` during construction.
1054    /// Therefore, you should generally access it via the
1055    /// `terminator()` or `terminator_mut()` methods. The only
1056    /// exception is that certain passes, such as `simplify_cfg`, swap
1057    /// out the terminator temporarily with `None` while they continue
1058    /// to recurse over the set of basic blocks.
1059    pub terminator: Option<Terminator>,
1060
1061    /// If true, this block lies on an unwind path. This is used
1062    /// during codegen where distinct kinds of basic blocks may be
1063    /// generated (particularly for MSVC cleanup). Unwind blocks must
1064    /// only branch to other unwind blocks.
1065    pub is_cleanup: bool,
1066}
1067
1068#[derive(Debug, Clone, PartialEq, Eq, Update)]
1069pub struct MirBody<'db> {
1070    pub basic_blocks: Arena<BasicBlock>,
1071    pub locals: Arena<Local>,
1072    pub start_block: BasicBlockId,
1073    pub owner: InferBodyId<'db>,
1074    pub binding_locals: ArenaMap<BindingId, LocalId>,
1075    pub upvar_locals: FxHashMap<BindingId, Vec<(LocalId, crate::closure_analysis::Place)>>,
1076    pub param_locals: Vec<LocalId>,
1077    /// This field stores the closures directly owned by this body. It is used
1078    /// in traversing every mir body.
1079    pub closures: Vec<InternedClosureId<'db>>,
1080}
1081
1082impl MirBody<'_> {
1083    pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
1084        self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
1085    }
1086
1087    fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
1088        fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
1089            match &mut op.kind {
1090                OperandKind::Copy(p) | OperandKind::Move(p) => {
1091                    f(p);
1092                }
1093                OperandKind::Constant { .. }
1094                | OperandKind::Static(_)
1095                | OperandKind::Allocation { .. } => (),
1096            }
1097        }
1098        for (_, block) in self.basic_blocks.iter_mut() {
1099            for statement in &mut block.statements {
1100                match &mut statement.kind {
1101                    StatementKind::Assign(p, r) => {
1102                        f(p);
1103                        match r {
1104                            Rvalue::ShallowInitBoxWithAlloc(_) => (),
1105                            Rvalue::ShallowInitBox(o, _)
1106                            | Rvalue::UnaryOp(_, o)
1107                            | Rvalue::Cast(_, o, _)
1108                            | Rvalue::Repeat(o, _)
1109                            | Rvalue::Use(o) => for_operand(o, &mut f),
1110                            Rvalue::CopyForDeref(p)
1111                            | Rvalue::Discriminant(p)
1112                            | Rvalue::Len(p)
1113                            | Rvalue::Ref(_, p) => f(p),
1114                            Rvalue::CheckedBinaryOp(_, o1, o2) => {
1115                                for_operand(o1, &mut f);
1116                                for_operand(o2, &mut f);
1117                            }
1118                            Rvalue::Aggregate(_, ops) => {
1119                                for op in ops.iter_mut() {
1120                                    for_operand(op, &mut f);
1121                                }
1122                            }
1123                            Rvalue::ThreadLocalRef(n)
1124                            | Rvalue::AddressOf(n)
1125                            | Rvalue::BinaryOp(n)
1126                            | Rvalue::NullaryOp(n) => match *n {},
1127                        }
1128                    }
1129                    StatementKind::FakeRead(p) | StatementKind::Deinit(p) => f(p),
1130                    StatementKind::StorageLive(_)
1131                    | StatementKind::StorageDead(_)
1132                    | StatementKind::Nop => (),
1133                }
1134            }
1135            match &mut block.terminator {
1136                Some(x) => match &mut x.kind {
1137                    TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
1138                    TerminatorKind::FalseEdge { .. }
1139                    | TerminatorKind::FalseUnwind { .. }
1140                    | TerminatorKind::Goto { .. }
1141                    | TerminatorKind::UnwindResume
1142                    | TerminatorKind::CoroutineDrop
1143                    | TerminatorKind::Abort
1144                    | TerminatorKind::Return
1145                    | TerminatorKind::Unreachable => (),
1146                    TerminatorKind::Drop { place, .. } => {
1147                        f(place);
1148                    }
1149                    TerminatorKind::DropAndReplace { place, value, .. } => {
1150                        f(place);
1151                        for_operand(value, &mut f);
1152                    }
1153                    TerminatorKind::Call { func, args, destination, .. } => {
1154                        for_operand(func, &mut f);
1155                        args.iter_mut().for_each(|x| for_operand(x, &mut f));
1156                        f(destination);
1157                    }
1158                    TerminatorKind::Assert { cond, .. } => {
1159                        for_operand(cond, &mut f);
1160                    }
1161                    TerminatorKind::Yield { value, resume_arg, .. } => {
1162                        for_operand(value, &mut f);
1163                        f(resume_arg);
1164                    }
1165                },
1166                None => (),
1167            }
1168        }
1169    }
1170
1171    fn shrink_to_fit(&mut self) {
1172        let MirBody {
1173            basic_blocks,
1174            locals,
1175            start_block: _,
1176            owner: _,
1177            binding_locals,
1178            upvar_locals,
1179            param_locals,
1180            closures,
1181        } = self;
1182        basic_blocks.shrink_to_fit();
1183        locals.shrink_to_fit();
1184        binding_locals.shrink_to_fit();
1185        upvar_locals.shrink_to_fit();
1186        param_locals.shrink_to_fit();
1187        closures.shrink_to_fit();
1188        for (_, b) in basic_blocks.iter_mut() {
1189            let BasicBlock { statements, terminator: _, is_cleanup: _ } = b;
1190            statements.shrink_to_fit();
1191        }
1192    }
1193}
1194
1195#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1196pub enum MirSpan {
1197    ExprId(ExprId),
1198    PatId(PatId),
1199    BindingId(BindingId),
1200    SelfParam,
1201    Unknown,
1202}
1203impl_from!(ExprId, PatId for MirSpan);
1204
1205impl From<&ExprId> for MirSpan {
1206    fn from(value: &ExprId) -> Self {
1207        (*value).into()
1208    }
1209}
1210
1211impl<'tcx> PlaceRef<'tcx> {
1212    /// If this place represents a local variable like `_X` with no
1213    /// projections, return `Some(_X)`.
1214    #[inline]
1215    pub fn as_local(&self) -> Option<LocalId> {
1216        match *self {
1217            PlaceRef { local, projection } if projection.as_slice().is_empty() => Some(local),
1218            _ => None,
1219        }
1220    }
1221}
1222
1223/// To determine the type of a place, we need to keep track of the variant that has been downcast to, in order to find the correct fields.
1224/// This type does that.
1225#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Hash, PartialEq, Eq)]
1226pub struct PlaceTy<'db> {
1227    pub ty: Ty<'db>,
1228    /// Downcast to a particular variant of an enum or a coroutine, if included.
1229    #[type_foldable(identity)]
1230    #[type_visitable(ignore)]
1231    pub variant_id: Option<VariantId>,
1232}
1233
1234impl<'db> PlaceTy<'db> {
1235    #[inline]
1236    pub fn from_ty(ty: Ty<'db>) -> PlaceTy<'db> {
1237        PlaceTy { ty, variant_id: None }
1238    }
1239
1240    pub fn multi_projection_ty(
1241        self,
1242        infcx: &InferCtxt<'db>,
1243        env: ParamEnv<'db>,
1244        elems: &[PlaceElem],
1245    ) -> PlaceTy<'db> {
1246        elems.iter().fold(self, |place_ty, elem| place_ty.projection_ty(infcx, elem, env))
1247    }
1248
1249    fn field_ty(
1250        infcx: &InferCtxt<'db>,
1251        self_ty: Ty<'db>,
1252        variant: Option<VariantId>,
1253        f: FieldIndex,
1254    ) -> Ty<'db> {
1255        if let Some(variant_id) = variant {
1256            match self_ty.kind() {
1257                TyKind::Adt(adt_def, args) if adt_def.is_enum() => {
1258                    infcx.interner.db().field_types(variant_id)[f.to_local_field_id()]
1259                        .ty()
1260                        .instantiate(infcx.interner, args)
1261                        .skip_norm_wip()
1262                }
1263                // FIXME TyKind::Coroutine...
1264                _ => panic!("can't downcast non-adt non-coroutine type: {self_ty:?}"),
1265            }
1266        } else {
1267            match self_ty.kind() {
1268                TyKind::Adt(adt_def, args) if !adt_def.is_enum() => {
1269                    let variant_id = VariantId::from_non_enum(adt_def.def_id()).unwrap();
1270                    infcx.interner.db().field_types(variant_id)[f.to_local_field_id()]
1271                        .ty()
1272                        .instantiate(infcx.interner, args)
1273                        .skip_norm_wip()
1274                }
1275                TyKind::Closure(_, args) => {
1276                    args.as_closure().tupled_upvars_ty().tuple_fields()[f.0 as usize]
1277                }
1278                // FIXME TyKind::Coroutine / TyKind::CoroutineClosure...
1279                TyKind::Tuple(tys) => tys
1280                    .get(f.0 as usize)
1281                    .cloned()
1282                    .unwrap_or_else(|| panic!("field {f:?} out of range: {self_ty:?}")),
1283                _ => panic!("can't project out of {self_ty:?}"),
1284            }
1285        }
1286    }
1287
1288    /// Convenience wrapper around `projection_ty_core` for `PlaceElem`.
1289    pub fn projection_ty<V: ::std::fmt::Debug + PartialEq>(
1290        self,
1291        infcx: &InferCtxt<'db>,
1292        elem: &ProjectionElem<V>,
1293        env: ParamEnv<'db>,
1294    ) -> PlaceTy<'db> {
1295        self.projection_ty_core(
1296            infcx.interner,
1297            elem,
1298            |ty| {
1299                if matches!(ty.kind(), TyKind::Alias(..)) {
1300                    let mut ocx = ObligationCtxt::new(infcx);
1301                    match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, ty) {
1302                        Ok(it) => it,
1303                        Err(_) => Ty::new_error(infcx.interner, ErrorGuaranteed),
1304                    }
1305                } else {
1306                    ty
1307                }
1308            },
1309            |self_ty, variant, field_id| Self::field_ty(infcx, self_ty, variant, field_id),
1310        )
1311    }
1312
1313    /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
1314    /// projects `place_ty` onto `elem`, returning the appropriate
1315    /// `Ty` or downcast variant corresponding to that projection.
1316    /// The `handle_field` callback must map a `FieldIndex` to its `Ty`
1317    pub fn projection_ty_core<V: PartialEq + ::std::fmt::Debug>(
1318        self,
1319        tcx: DbInterner<'db>,
1320        elem: &ProjectionElem<V>,
1321        mut structurally_normalize: impl FnMut(Ty<'db>) -> Ty<'db>,
1322        mut handle_field: impl FnMut(Ty<'db>, Option<VariantId>, FieldIndex /*, T*/) -> Ty<'db>,
1323    ) -> PlaceTy<'db> {
1324        // we only bail on mir building when there are type mismatches
1325        // but error types may pop up resulting in us still attempting to build the mir
1326        // so just propagate the error type
1327        if self.ty.is_ty_error() {
1328            return PlaceTy::from_ty(Ty::new_error(tcx, ErrorGuaranteed));
1329        }
1330        if self.variant_id.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
1331            panic!("cannot use non field projection on downcasted place")
1332        }
1333        match *elem {
1334            ProjectionElem::Deref => {
1335                let ty = structurally_normalize(self.ty).builtin_deref(true).unwrap_or_else(|| {
1336                    panic!("deref projection of non-dereferenceable ty {:?}", self)
1337                });
1338                PlaceTy::from_ty(ty)
1339            }
1340            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
1341                PlaceTy::from_ty(structurally_normalize(self.ty).builtin_index().unwrap())
1342            }
1343            ProjectionElem::Subslice { from, to /*, from_end*/ } => {
1344                PlaceTy::from_ty(match structurally_normalize(self.ty).kind() {
1345                    TyKind::Slice(..) => self.ty,
1346                    TyKind::Array(inner, _) /*if !from_end*/ => Ty::new_array_opt(tcx, inner, to.checked_sub(from).map(|x| x.into())),
1347                    // TyKind::Array(inner, size) if from_end => {
1348                    //     let size = size
1349                    //         .try_to_target_usize(tcx)
1350                    //         .expect("expected subslice projection on fixed-size array");
1351                    //     let len = size - from - to;
1352                    //     Ty::new_array(tcx, *inner, len)
1353                    // }
1354                    _ => panic!("cannot subslice non-array type: `{:?}`", self),
1355                })
1356            }
1357            ProjectionElem::Downcast(index) => PlaceTy { ty: self.ty, variant_id: Some(index) },
1358            ProjectionElem::Field(f) => {
1359                PlaceTy::from_ty(handle_field(structurally_normalize(self.ty), self.variant_id, f))
1360            }
1361        }
1362    }
1363}