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