| use std::io; | 
 |  | 
 | use serde::Serialize; | 
 |  | 
 | use crate::compiler_interface::with; | 
 | use crate::mir::pretty::function_body; | 
 | use crate::ty::{ | 
 |     AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, | 
 |     Region, RigidTy, Ty, TyConst, TyKind, VariantIdx, | 
 | }; | 
 | use crate::{Error, Opaque, Span, Symbol}; | 
 |  | 
 | /// The SMIR representation of a single function. | 
 | #[derive(Clone, Debug, Serialize)] | 
 | pub struct Body { | 
 |     pub blocks: Vec<BasicBlock>, | 
 |  | 
 |     /// Declarations of locals within the function. | 
 |     /// | 
 |     /// The first local is the return value pointer, followed by `arg_count` | 
 |     /// locals for the function arguments, followed by any user-declared | 
 |     /// variables and temporaries. | 
 |     pub(super) locals: LocalDecls, | 
 |  | 
 |     /// The number of arguments this function takes. | 
 |     pub(super) arg_count: usize, | 
 |  | 
 |     /// Debug information pertaining to user variables, including captures. | 
 |     pub var_debug_info: Vec<VarDebugInfo>, | 
 |  | 
 |     /// Mark an argument (which must be a tuple) as getting passed as its individual components. | 
 |     /// | 
 |     /// This is used for the "rust-call" ABI such as closures. | 
 |     pub(super) spread_arg: Option<Local>, | 
 |  | 
 |     /// The span that covers the entire function body. | 
 |     pub span: Span, | 
 | } | 
 |  | 
 | pub type BasicBlockIdx = usize; | 
 |  | 
 | impl Body { | 
 |     /// Constructs a `Body`. | 
 |     /// | 
 |     /// A constructor is required to build a `Body` from outside the crate | 
 |     /// because the `arg_count` and `locals` fields are private. | 
 |     pub fn new( | 
 |         blocks: Vec<BasicBlock>, | 
 |         locals: LocalDecls, | 
 |         arg_count: usize, | 
 |         var_debug_info: Vec<VarDebugInfo>, | 
 |         spread_arg: Option<Local>, | 
 |         span: Span, | 
 |     ) -> Self { | 
 |         // If locals doesn't contain enough entries, it can lead to panics in | 
 |         // `ret_local`, `arg_locals`, and `inner_locals`. | 
 |         assert!( | 
 |             locals.len() > arg_count, | 
 |             "A Body must contain at least a local for the return value and each of the function's arguments" | 
 |         ); | 
 |         Self { blocks, locals, arg_count, var_debug_info, spread_arg, span } | 
 |     } | 
 |  | 
 |     /// Return local that holds this function's return value. | 
 |     pub fn ret_local(&self) -> &LocalDecl { | 
 |         &self.locals[RETURN_LOCAL] | 
 |     } | 
 |  | 
 |     /// Locals in `self` that correspond to this function's arguments. | 
 |     pub fn arg_locals(&self) -> &[LocalDecl] { | 
 |         &self.locals[1..][..self.arg_count] | 
 |     } | 
 |  | 
 |     /// Inner locals for this function. These are the locals that are | 
 |     /// neither the return local nor the argument locals. | 
 |     pub fn inner_locals(&self) -> &[LocalDecl] { | 
 |         &self.locals[self.arg_count + 1..] | 
 |     } | 
 |  | 
 |     /// Returns a mutable reference to the local that holds this function's return value. | 
 |     pub(crate) fn ret_local_mut(&mut self) -> &mut LocalDecl { | 
 |         &mut self.locals[RETURN_LOCAL] | 
 |     } | 
 |  | 
 |     /// Returns a mutable slice of locals corresponding to this function's arguments. | 
 |     pub(crate) fn arg_locals_mut(&mut self) -> &mut [LocalDecl] { | 
 |         &mut self.locals[1..][..self.arg_count] | 
 |     } | 
 |  | 
 |     /// Returns a mutable slice of inner locals for this function. | 
 |     /// Inner locals are those that are neither the return local nor the argument locals. | 
 |     pub(crate) fn inner_locals_mut(&mut self) -> &mut [LocalDecl] { | 
 |         &mut self.locals[self.arg_count + 1..] | 
 |     } | 
 |  | 
 |     /// Convenience function to get all the locals in this function. | 
 |     /// | 
 |     /// Locals are typically accessed via the more specific methods `ret_local`, | 
 |     /// `arg_locals`, and `inner_locals`. | 
 |     pub fn locals(&self) -> &[LocalDecl] { | 
 |         &self.locals | 
 |     } | 
 |  | 
 |     /// Get the local declaration for this local. | 
 |     pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> { | 
 |         self.locals.get(local) | 
 |     } | 
 |  | 
 |     /// Get an iterator for all local declarations. | 
 |     pub fn local_decls(&self) -> impl Iterator<Item = (Local, &LocalDecl)> { | 
 |         self.locals.iter().enumerate() | 
 |     } | 
 |  | 
 |     /// Emit the body using the provided name for the signature. | 
 |     pub fn dump<W: io::Write>(&self, w: &mut W, fn_name: &str) -> io::Result<()> { | 
 |         function_body(w, self, fn_name) | 
 |     } | 
 |  | 
 |     pub fn spread_arg(&self) -> Option<Local> { | 
 |         self.spread_arg | 
 |     } | 
 | } | 
 |  | 
 | type LocalDecls = Vec<LocalDecl>; | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct LocalDecl { | 
 |     pub ty: Ty, | 
 |     pub span: Span, | 
 |     pub mutability: Mutability, | 
 | } | 
 |  | 
 | #[derive(Clone, PartialEq, Eq, Debug, Serialize)] | 
 | pub struct BasicBlock { | 
 |     pub statements: Vec<Statement>, | 
 |     pub terminator: Terminator, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct Terminator { | 
 |     pub kind: TerminatorKind, | 
 |     pub span: Span, | 
 | } | 
 |  | 
 | impl Terminator { | 
 |     pub fn successors(&self) -> Successors { | 
 |         self.kind.successors() | 
 |     } | 
 | } | 
 |  | 
 | pub type Successors = Vec<BasicBlockIdx>; | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum TerminatorKind { | 
 |     Goto { | 
 |         target: BasicBlockIdx, | 
 |     }, | 
 |     SwitchInt { | 
 |         discr: Operand, | 
 |         targets: SwitchTargets, | 
 |     }, | 
 |     Resume, | 
 |     Abort, | 
 |     Return, | 
 |     Unreachable, | 
 |     Drop { | 
 |         place: Place, | 
 |         target: BasicBlockIdx, | 
 |         unwind: UnwindAction, | 
 |     }, | 
 |     Call { | 
 |         func: Operand, | 
 |         args: Vec<Operand>, | 
 |         destination: Place, | 
 |         target: Option<BasicBlockIdx>, | 
 |         unwind: UnwindAction, | 
 |     }, | 
 |     Assert { | 
 |         cond: Operand, | 
 |         expected: bool, | 
 |         msg: AssertMessage, | 
 |         target: BasicBlockIdx, | 
 |         unwind: UnwindAction, | 
 |     }, | 
 |     InlineAsm { | 
 |         template: String, | 
 |         operands: Vec<InlineAsmOperand>, | 
 |         options: String, | 
 |         line_spans: String, | 
 |         destination: Option<BasicBlockIdx>, | 
 |         unwind: UnwindAction, | 
 |     }, | 
 | } | 
 |  | 
 | impl TerminatorKind { | 
 |     pub fn successors(&self) -> Successors { | 
 |         use self::TerminatorKind::*; | 
 |         match *self { | 
 |             Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. } | 
 |             | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. } | 
 |             | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. } | 
 |             | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => { | 
 |                 vec![t, u] | 
 |             } | 
 |             Goto { target: t } | 
 |             | Call { target: None, unwind: UnwindAction::Cleanup(t), .. } | 
 |             | Call { target: Some(t), unwind: _, .. } | 
 |             | Drop { target: t, unwind: _, .. } | 
 |             | Assert { target: t, unwind: _, .. } | 
 |             | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } | 
 |             | InlineAsm { destination: Some(t), unwind: _, .. } => { | 
 |                 vec![t] | 
 |             } | 
 |  | 
 |             Return | 
 |             | Resume | 
 |             | Abort | 
 |             | Unreachable | 
 |             | Call { target: None, unwind: _, .. } | 
 |             | InlineAsm { destination: None, unwind: _, .. } => { | 
 |                 vec![] | 
 |             } | 
 |             SwitchInt { ref targets, .. } => targets.all_targets(), | 
 |         } | 
 |     } | 
 |  | 
 |     pub fn unwind(&self) -> Option<&UnwindAction> { | 
 |         match *self { | 
 |             TerminatorKind::Goto { .. } | 
 |             | TerminatorKind::Return | 
 |             | TerminatorKind::Unreachable | 
 |             | TerminatorKind::Resume | 
 |             | TerminatorKind::Abort | 
 |             | TerminatorKind::SwitchInt { .. } => None, | 
 |             TerminatorKind::Call { ref unwind, .. } | 
 |             | TerminatorKind::Assert { ref unwind, .. } | 
 |             | TerminatorKind::Drop { ref unwind, .. } | 
 |             | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct InlineAsmOperand { | 
 |     pub in_value: Option<Operand>, | 
 |     pub out_place: Option<Place>, | 
 |     // This field has a raw debug representation of MIR's InlineAsmOperand. | 
 |     // For now we care about place/operand + the rest in a debug format. | 
 |     pub raw_rpr: String, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum UnwindAction { | 
 |     Continue, | 
 |     Unreachable, | 
 |     Terminate, | 
 |     Cleanup(BasicBlockIdx), | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum AssertMessage { | 
 |     BoundsCheck { len: Operand, index: Operand }, | 
 |     Overflow(BinOp, Operand, Operand), | 
 |     OverflowNeg(Operand), | 
 |     DivisionByZero(Operand), | 
 |     RemainderByZero(Operand), | 
 |     ResumedAfterReturn(CoroutineKind), | 
 |     ResumedAfterPanic(CoroutineKind), | 
 |     MisalignedPointerDereference { required: Operand, found: Operand }, | 
 |     NullPointerDereference, | 
 | } | 
 |  | 
 | impl AssertMessage { | 
 |     pub fn description(&self) -> Result<&'static str, Error> { | 
 |         match self { | 
 |             AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"), | 
 |             AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"), | 
 |             AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"), | 
 |             AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"), | 
 |             AssertMessage::Overflow(BinOp::Rem, _, _) => { | 
 |                 Ok("attempt to calculate the remainder with overflow") | 
 |             } | 
 |             AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"), | 
 |             AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"), | 
 |             AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"), | 
 |             AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)), | 
 |             AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"), | 
 |             AssertMessage::RemainderByZero(_) => { | 
 |                 Ok("attempt to calculate the remainder with a divisor of zero") | 
 |             } | 
 |             AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { | 
 |                 Ok("coroutine resumed after completion") | 
 |             } | 
 |             AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( | 
 |                 CoroutineDesugaring::Async, | 
 |                 _, | 
 |             )) => Ok("`async fn` resumed after completion"), | 
 |             AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( | 
 |                 CoroutineDesugaring::Gen, | 
 |                 _, | 
 |             )) => Ok("`async gen fn` resumed after completion"), | 
 |             AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( | 
 |                 CoroutineDesugaring::AsyncGen, | 
 |                 _, | 
 |             )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"), | 
 |             AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { | 
 |                 Ok("coroutine resumed after panicking") | 
 |             } | 
 |             AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( | 
 |                 CoroutineDesugaring::Async, | 
 |                 _, | 
 |             )) => Ok("`async fn` resumed after panicking"), | 
 |             AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( | 
 |                 CoroutineDesugaring::Gen, | 
 |                 _, | 
 |             )) => Ok("`async gen fn` resumed after panicking"), | 
 |             AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( | 
 |                 CoroutineDesugaring::AsyncGen, | 
 |                 _, | 
 |             )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"), | 
 |  | 
 |             AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"), | 
 |             AssertMessage::MisalignedPointerDereference { .. } => { | 
 |                 Ok("misaligned pointer dereference") | 
 |             } | 
 |             AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum BinOp { | 
 |     Add, | 
 |     AddUnchecked, | 
 |     Sub, | 
 |     SubUnchecked, | 
 |     Mul, | 
 |     MulUnchecked, | 
 |     Div, | 
 |     Rem, | 
 |     BitXor, | 
 |     BitAnd, | 
 |     BitOr, | 
 |     Shl, | 
 |     ShlUnchecked, | 
 |     Shr, | 
 |     ShrUnchecked, | 
 |     Eq, | 
 |     Lt, | 
 |     Le, | 
 |     Ne, | 
 |     Ge, | 
 |     Gt, | 
 |     Cmp, | 
 |     Offset, | 
 | } | 
 |  | 
 | impl BinOp { | 
 |     /// Return the type of this operation for the given input Ty. | 
 |     /// This function does not perform type checking, and it currently doesn't handle SIMD. | 
 |     pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { | 
 |         with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum UnOp { | 
 |     Not, | 
 |     Neg, | 
 |     PtrMetadata, | 
 | } | 
 |  | 
 | impl UnOp { | 
 |     /// Return the type of this operation for the given input Ty. | 
 |     /// This function does not perform type checking, and it currently doesn't handle SIMD. | 
 |     pub fn ty(&self, arg_ty: Ty) -> Ty { | 
 |         with(|ctx| ctx.unop_ty(*self, arg_ty)) | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum CoroutineKind { | 
 |     Desugared(CoroutineDesugaring, CoroutineSource), | 
 |     Coroutine(Movability), | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum CoroutineSource { | 
 |     Block, | 
 |     Closure, | 
 |     Fn, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum CoroutineDesugaring { | 
 |     Async, | 
 |  | 
 |     Gen, | 
 |  | 
 |     AsyncGen, | 
 | } | 
 |  | 
 | pub(crate) type LocalDefId = Opaque; | 
 | /// The rustc coverage data structures are heavily tied to internal details of the | 
 | /// coverage implementation that are likely to change, and are unlikely to be | 
 | /// useful to third-party tools for the foreseeable future. | 
 | pub(crate) type Coverage = Opaque; | 
 |  | 
 | /// The FakeReadCause describes the type of pattern why a FakeRead statement exists. | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum FakeReadCause { | 
 |     ForMatchGuard, | 
 |     ForMatchedPlace(LocalDefId), | 
 |     ForGuardBinding, | 
 |     ForLet(LocalDefId), | 
 |     ForIndex, | 
 | } | 
 |  | 
 | /// Describes what kind of retag is to be performed | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] | 
 | pub enum RetagKind { | 
 |     FnEntry, | 
 |     TwoPhase, | 
 |     Raw, | 
 |     Default, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] | 
 | pub enum Variance { | 
 |     Covariant, | 
 |     Invariant, | 
 |     Contravariant, | 
 |     Bivariant, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct CopyNonOverlapping { | 
 |     pub src: Operand, | 
 |     pub dst: Operand, | 
 |     pub count: Operand, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum NonDivergingIntrinsic { | 
 |     Assume(Operand), | 
 |     CopyNonOverlapping(CopyNonOverlapping), | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct Statement { | 
 |     pub kind: StatementKind, | 
 |     pub span: Span, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum StatementKind { | 
 |     Assign(Place, Rvalue), | 
 |     FakeRead(FakeReadCause, Place), | 
 |     SetDiscriminant { place: Place, variant_index: VariantIdx }, | 
 |     Deinit(Place), | 
 |     StorageLive(Local), | 
 |     StorageDead(Local), | 
 |     Retag(RetagKind, Place), | 
 |     PlaceMention(Place), | 
 |     AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance }, | 
 |     Coverage(Coverage), | 
 |     Intrinsic(NonDivergingIntrinsic), | 
 |     ConstEvalCounter, | 
 |     Nop, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum Rvalue { | 
 |     /// Creates a pointer with the indicated mutability to the place. | 
 |     /// | 
 |     /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like | 
 |     /// `&raw v` or `addr_of!(v)`. | 
 |     AddressOf(RawPtrKind, Place), | 
 |  | 
 |     /// Creates an aggregate value, like a tuple or struct. | 
 |     /// | 
 |     /// This is needed because dataflow analysis needs to distinguish | 
 |     /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` | 
 |     /// has a destructor. | 
 |     /// | 
 |     /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After | 
 |     /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. | 
 |     Aggregate(AggregateKind, Vec<Operand>), | 
 |  | 
 |     /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second | 
 |     ///   parameter may be a `usize` as well. | 
 |     /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, | 
 |     ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be | 
 |     ///   matching, up to the usual caveat of the lifetimes in function pointers. | 
 |     /// * Left and right shift operations accept signed or unsigned integers not necessarily of the | 
 |     ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is | 
 |     ///   truncated as needed. | 
 |     /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching | 
 |     ///   types and return a value of that type. | 
 |     /// * The remaining operations accept signed integers, unsigned integers, or floats with | 
 |     ///   matching types and return a value of that type. | 
 |     BinaryOp(BinOp, Operand, Operand), | 
 |  | 
 |     /// Performs essentially all of the casts that can be performed via `as`. | 
 |     /// | 
 |     /// This allows for casts from/to a variety of types. | 
 |     Cast(CastKind, Operand, Ty), | 
 |  | 
 |     /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. | 
 |     /// | 
 |     /// For addition, subtraction, and multiplication on integers the error condition is set when | 
 |     /// the infinite precision result would not be equal to the actual result. | 
 |     CheckedBinaryOp(BinOp, Operand, Operand), | 
 |  | 
 |     /// A CopyForDeref is equivalent to a read from a place. | 
 |     /// When such a read happens, it is guaranteed that the only use of the returned value is a | 
 |     /// deref operation, immediately followed by one or more projections. | 
 |     CopyForDeref(Place), | 
 |  | 
 |     /// Computes the discriminant of the place, returning it as an integer. | 
 |     /// Returns zero for types without discriminant. | 
 |     /// | 
 |     /// The validity requirements for the underlying value are undecided for this rvalue, see | 
 |     /// [#91095]. Note too that the value of the discriminant is not the same thing as the | 
 |     /// variant index; | 
 |     /// | 
 |     /// [#91095]: https://github.com/rust-lang/rust/issues/91095 | 
 |     Discriminant(Place), | 
 |  | 
 |     /// Yields the length of the place, as a `usize`. | 
 |     /// | 
 |     /// If the type of the place is an array, this is the array length. For slices (`[T]`, not | 
 |     /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is | 
 |     /// ill-formed for places of other types. | 
 |     Len(Place), | 
 |  | 
 |     /// Creates a reference to the place. | 
 |     Ref(Region, BorrowKind, Place), | 
 |  | 
 |     /// Creates an array where each element is the value of the operand. | 
 |     /// | 
 |     /// This is the cause of a bug in the case where the repetition count is zero because the value | 
 |     /// is not dropped, see [#74836]. | 
 |     /// | 
 |     /// Corresponds to source code like `[x; 32]`. | 
 |     /// | 
 |     /// [#74836]: https://github.com/rust-lang/rust/issues/74836 | 
 |     Repeat(Operand, TyConst), | 
 |  | 
 |     /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`. | 
 |     /// | 
 |     /// This is different from a normal transmute because dataflow analysis will treat the box as | 
 |     /// initialized but its content as uninitialized. Like other pointer casts, this in general | 
 |     /// affects alias analysis. | 
 |     ShallowInitBox(Operand, Ty), | 
 |  | 
 |     /// Creates a pointer/reference to the given thread local. | 
 |     /// | 
 |     /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a | 
 |     /// `*const T`, and if neither of those apply a `&T`. | 
 |     /// | 
 |     /// **Note:** This is a runtime operation that actually executes code and is in this sense more | 
 |     /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to | 
 |     /// SIGILL for some reason that I (JakobDegen) never got a chance to look into. | 
 |     /// | 
 |     /// **Needs clarification**: Are there weird additional semantics here related to the runtime | 
 |     /// nature of this operation? | 
 |     ThreadLocalRef(crate::CrateItem), | 
 |  | 
 |     /// Computes a value as described by the operation. | 
 |     NullaryOp(NullOp, Ty), | 
 |  | 
 |     /// Exactly like `BinaryOp`, but less operands. | 
 |     /// | 
 |     /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; | 
 |     /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds | 
 |     /// return a value with the same type as their operand. | 
 |     UnaryOp(UnOp, Operand), | 
 |  | 
 |     /// Yields the operand unchanged | 
 |     Use(Operand), | 
 | } | 
 |  | 
 | impl Rvalue { | 
 |     pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> { | 
 |         match self { | 
 |             Rvalue::Use(operand) => operand.ty(locals), | 
 |             Rvalue::Repeat(operand, count) => { | 
 |                 Ok(Ty::new_array_with_const_len(operand.ty(locals)?, count.clone())) | 
 |             } | 
 |             Rvalue::ThreadLocalRef(did) => Ok(did.ty()), | 
 |             Rvalue::Ref(reg, bk, place) => { | 
 |                 let place_ty = place.ty(locals)?; | 
 |                 Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) | 
 |             } | 
 |             Rvalue::AddressOf(mutability, place) => { | 
 |                 let place_ty = place.ty(locals)?; | 
 |                 Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) | 
 |             } | 
 |             Rvalue::Len(..) => Ok(Ty::usize_ty()), | 
 |             Rvalue::Cast(.., ty) => Ok(*ty), | 
 |             Rvalue::BinaryOp(op, lhs, rhs) => { | 
 |                 let lhs_ty = lhs.ty(locals)?; | 
 |                 let rhs_ty = rhs.ty(locals)?; | 
 |                 Ok(op.ty(lhs_ty, rhs_ty)) | 
 |             } | 
 |             Rvalue::CheckedBinaryOp(op, lhs, rhs) => { | 
 |                 let lhs_ty = lhs.ty(locals)?; | 
 |                 let rhs_ty = rhs.ty(locals)?; | 
 |                 let ty = op.ty(lhs_ty, rhs_ty); | 
 |                 Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) | 
 |             } | 
 |             Rvalue::UnaryOp(op, operand) => { | 
 |                 let arg_ty = operand.ty(locals)?; | 
 |                 Ok(op.ty(arg_ty)) | 
 |             } | 
 |             Rvalue::Discriminant(place) => { | 
 |                 let place_ty = place.ty(locals)?; | 
 |                 place_ty | 
 |                     .kind() | 
 |                     .discriminant_ty() | 
 |                     .ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}")) | 
 |             } | 
 |             Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { | 
 |                 Ok(Ty::usize_ty()) | 
 |             } | 
 |             Rvalue::NullaryOp(NullOp::ContractChecks, _) | 
 |             | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), | 
 |             Rvalue::Aggregate(ak, ops) => match *ak { | 
 |                 AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), | 
 |                 AggregateKind::Tuple => Ok(Ty::new_tuple( | 
 |                     &ops.iter().map(|op| op.ty(locals)).collect::<Result<Vec<_>, _>>()?, | 
 |                 )), | 
 |                 AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), | 
 |                 AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), | 
 |                 AggregateKind::Coroutine(def, ref args, mov) => { | 
 |                     Ok(Ty::new_coroutine(def, args.clone(), mov)) | 
 |                 } | 
 |                 AggregateKind::CoroutineClosure(def, ref args) => { | 
 |                     Ok(Ty::new_coroutine_closure(def, args.clone())) | 
 |                 } | 
 |                 AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), | 
 |             }, | 
 |             Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), | 
 |             Rvalue::CopyForDeref(place) => place.ty(locals), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum AggregateKind { | 
 |     Array(Ty), | 
 |     Tuple, | 
 |     Adt(AdtDef, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>), | 
 |     Closure(ClosureDef, GenericArgs), | 
 |     // FIXME(stable_mir): Movability here is redundant | 
 |     Coroutine(CoroutineDef, GenericArgs, Movability), | 
 |     CoroutineClosure(CoroutineClosureDef, GenericArgs), | 
 |     RawPtr(Ty, Mutability), | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum Operand { | 
 |     Copy(Place), | 
 |     Move(Place), | 
 |     Constant(ConstOperand), | 
 | } | 
 |  | 
 | #[derive(Clone, Eq, PartialEq, Serialize)] | 
 | pub struct Place { | 
 |     pub local: Local, | 
 |     /// projection out of a place (access a field, deref a pointer, etc) | 
 |     pub projection: Vec<ProjectionElem>, | 
 | } | 
 |  | 
 | impl From<Local> for Place { | 
 |     fn from(local: Local) -> Self { | 
 |         Place { local, projection: vec![] } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct ConstOperand { | 
 |     pub span: Span, | 
 |     pub user_ty: Option<UserTypeAnnotationIndex>, | 
 |     pub const_: MirConst, | 
 | } | 
 |  | 
 | /// Debug information pertaining to a user variable. | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct VarDebugInfo { | 
 |     /// The variable name. | 
 |     pub name: Symbol, | 
 |  | 
 |     /// Source info of the user variable, including the scope | 
 |     /// within which the variable is visible (to debuginfo). | 
 |     pub source_info: SourceInfo, | 
 |  | 
 |     /// The user variable's data is split across several fragments, | 
 |     /// each described by a `VarDebugInfoFragment`. | 
 |     pub composite: Option<VarDebugInfoFragment>, | 
 |  | 
 |     /// Where the data for this user variable is to be found. | 
 |     pub value: VarDebugInfoContents, | 
 |  | 
 |     /// When present, indicates what argument number this variable is in the function that it | 
 |     /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the | 
 |     /// argument number in the original function before it was inlined. | 
 |     pub argument_index: Option<u16>, | 
 | } | 
 |  | 
 | impl VarDebugInfo { | 
 |     /// Return a local variable if this info is related to one. | 
 |     pub fn local(&self) -> Option<Local> { | 
 |         match &self.value { | 
 |             VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local), | 
 |             VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None, | 
 |         } | 
 |     } | 
 |  | 
 |     /// Return a constant if this info is related to one. | 
 |     pub fn constant(&self) -> Option<&ConstOperand> { | 
 |         match &self.value { | 
 |             VarDebugInfoContents::Place(_) => None, | 
 |             VarDebugInfoContents::Const(const_op) => Some(const_op), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | pub type SourceScope = u32; | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct SourceInfo { | 
 |     pub span: Span, | 
 |     pub scope: SourceScope, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct VarDebugInfoFragment { | 
 |     pub ty: Ty, | 
 |     pub projection: Vec<ProjectionElem>, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum VarDebugInfoContents { | 
 |     Place(Place), | 
 |     Const(ConstOperand), | 
 | } | 
 |  | 
 | // In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This | 
 | // is so it can be used for both Places (for which the projection elements are of type | 
 | // ProjectionElem<Local, Ty>) and user-provided type annotations (for which the projection elements | 
 | // are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use | 
 | // ProjectionElem for Places. | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum ProjectionElem { | 
 |     /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. | 
 |     Deref, | 
 |  | 
 |     /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is | 
 |     /// referenced by source-order index rather than the name of the field. The fields type is also | 
 |     /// given. | 
 |     Field(FieldIdx, Ty), | 
 |  | 
 |     /// Index into a slice/array. The value of the index is computed at runtime using the `V` | 
 |     /// argument. | 
 |     /// | 
 |     /// Note that this does not also dereference, and so it does not exactly correspond to slice | 
 |     /// indexing in Rust. In other words, in the below Rust code: | 
 |     /// | 
 |     /// ```rust | 
 |     /// let x = &[1, 2, 3, 4]; | 
 |     /// let i = 2; | 
 |     /// x[i]; | 
 |     /// ``` | 
 |     /// | 
 |     /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same | 
 |     /// thing is true of the `ConstantIndex` and `Subslice` projections below. | 
 |     Index(Local), | 
 |  | 
 |     /// Index into a slice/array given by offsets. | 
 |     /// | 
 |     /// These indices are generated by slice patterns. Easiest to explain by example: | 
 |     /// | 
 |     /// ```ignore (illustrative) | 
 |     /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, | 
 |     /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, | 
 |     /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, | 
 |     /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, | 
 |     /// ``` | 
 |     ConstantIndex { | 
 |         /// index or -index (in Python terms), depending on from_end | 
 |         offset: u64, | 
 |         /// The thing being indexed must be at least this long -- otherwise, the | 
 |         /// projection is UB. | 
 |         /// | 
 |         /// For arrays this is always the exact length. | 
 |         min_length: u64, | 
 |         /// Counting backwards from end? This is always false when indexing an | 
 |         /// array. | 
 |         from_end: bool, | 
 |     }, | 
 |  | 
 |     /// Projects a slice from the base place. | 
 |     /// | 
 |     /// These indices are generated by slice patterns. If `from_end` is true, this represents | 
 |     /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`. | 
 |     Subslice { | 
 |         from: u64, | 
 |         to: u64, | 
 |         /// Whether `to` counts from the start or end of the array/slice. | 
 |         from_end: bool, | 
 |     }, | 
 |  | 
 |     /// "Downcast" to a variant of an enum or a coroutine. | 
 |     Downcast(VariantIdx), | 
 |  | 
 |     /// Like an explicit cast from an opaque type to a concrete type, but without | 
 |     /// requiring an intermediate variable. | 
 |     OpaqueCast(Ty), | 
 |  | 
 |     /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where | 
 |     /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping | 
 |     /// explicit during optimizations and codegen. | 
 |     /// | 
 |     /// This projection doesn't impact the runtime behavior of the program except for potentially changing | 
 |     /// some type metadata of the interpreter or codegen backend. | 
 |     Subtype(Ty), | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct UserTypeProjection { | 
 |     pub base: UserTypeAnnotationIndex, | 
 |  | 
 |     pub projection: Opaque, | 
 | } | 
 |  | 
 | pub type Local = usize; | 
 |  | 
 | pub const RETURN_LOCAL: Local = 0; | 
 |  | 
 | /// The source-order index of a field in a variant. | 
 | /// | 
 | /// For example, in the following types, | 
 | /// ```ignore(illustrative) | 
 | /// enum Demo1 { | 
 | ///    Variant0 { a: bool, b: i32 }, | 
 | ///    Variant1 { c: u8, d: u64 }, | 
 | /// } | 
 | /// struct Demo2 { e: u8, f: u16, g: u8 } | 
 | /// ``` | 
 | /// `a`'s `FieldIdx` is `0`, | 
 | /// `b`'s `FieldIdx` is `1`, | 
 | /// `c`'s `FieldIdx` is `0`, and | 
 | /// `g`'s `FieldIdx` is `2`. | 
 | pub type FieldIdx = usize; | 
 |  | 
 | type UserTypeAnnotationIndex = usize; | 
 |  | 
 | /// The possible branch sites of a [TerminatorKind::SwitchInt]. | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub struct SwitchTargets { | 
 |     /// The conditional branches where the first element represents the value that guards this | 
 |     /// branch, and the second element is the branch target. | 
 |     branches: Vec<(u128, BasicBlockIdx)>, | 
 |     /// The `otherwise` branch which will be taken in case none of the conditional branches are | 
 |     /// satisfied. | 
 |     otherwise: BasicBlockIdx, | 
 | } | 
 |  | 
 | impl SwitchTargets { | 
 |     /// All possible targets including the `otherwise` target. | 
 |     pub fn all_targets(&self) -> Successors { | 
 |         self.branches.iter().map(|(_, target)| *target).chain(Some(self.otherwise)).collect() | 
 |     } | 
 |  | 
 |     /// The `otherwise` branch target. | 
 |     pub fn otherwise(&self) -> BasicBlockIdx { | 
 |         self.otherwise | 
 |     } | 
 |  | 
 |     /// The conditional targets which are only taken if the pattern matches the given value. | 
 |     pub fn branches(&self) -> impl Iterator<Item = (u128, BasicBlockIdx)> { | 
 |         self.branches.iter().copied() | 
 |     } | 
 |  | 
 |     /// The number of targets including `otherwise`. | 
 |     pub fn len(&self) -> usize { | 
 |         self.branches.len() + 1 | 
 |     } | 
 |  | 
 |     /// Create a new SwitchTargets from the given branches and `otherwise` target. | 
 |     pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets { | 
 |         SwitchTargets { branches, otherwise } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum BorrowKind { | 
 |     /// Data must be immutable and is aliasable. | 
 |     Shared, | 
 |  | 
 |     /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either | 
 |     /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). | 
 |     Fake(FakeBorrowKind), | 
 |  | 
 |     /// Data is mutable and not aliasable. | 
 |     Mut { | 
 |         /// `true` if this borrow arose from method-call auto-ref | 
 |         kind: MutBorrowKind, | 
 |     }, | 
 | } | 
 |  | 
 | impl BorrowKind { | 
 |     pub fn to_mutable_lossy(self) -> Mutability { | 
 |         match self { | 
 |             BorrowKind::Mut { .. } => Mutability::Mut, | 
 |             BorrowKind::Shared => Mutability::Not, | 
 |             // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. | 
 |             BorrowKind::Fake(_) => Mutability::Not, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum RawPtrKind { | 
 |     Mut, | 
 |     Const, | 
 |     FakeForPtrMetadata, | 
 | } | 
 |  | 
 | impl RawPtrKind { | 
 |     pub fn to_mutable_lossy(self) -> Mutability { | 
 |         match self { | 
 |             RawPtrKind::Mut { .. } => Mutability::Mut, | 
 |             RawPtrKind::Const => Mutability::Not, | 
 |             // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. | 
 |             RawPtrKind::FakeForPtrMetadata => Mutability::Not, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum MutBorrowKind { | 
 |     Default, | 
 |     TwoPhaseBorrow, | 
 |     ClosureCapture, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum FakeBorrowKind { | 
 |     /// A shared (deep) borrow. Data must be immutable and is aliasable. | 
 |     Deep, | 
 |     /// The immediately borrowed place must be immutable, but projections from | 
 |     /// it don't need to be. This is used to prevent match guards from replacing | 
 |     /// the scrutinee. For example, a fake borrow of `a.b` doesn't | 
 |     /// conflict with a mutable borrow of `a.b.c`. | 
 |     Shallow, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] | 
 | pub enum Mutability { | 
 |     Not, | 
 |     Mut, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum Safety { | 
 |     Safe, | 
 |     Unsafe, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum PointerCoercion { | 
 |     /// Go from a fn-item type to a fn-pointer type. | 
 |     ReifyFnPointer, | 
 |  | 
 |     /// Go from a safe fn pointer to an unsafe fn pointer. | 
 |     UnsafeFnPointer, | 
 |  | 
 |     /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer. | 
 |     /// It cannot convert a closure that requires unsafe. | 
 |     ClosureFnPointer(Safety), | 
 |  | 
 |     /// Go from a mut raw pointer to a const raw pointer. | 
 |     MutToConstPointer, | 
 |  | 
 |     /// Go from `*const [T; N]` to `*const T` | 
 |     ArrayToPointer, | 
 |  | 
 |     /// Unsize a pointer/reference value, e.g., `&[T; n]` to | 
 |     /// `&[T]`. Note that the source could be a thin or wide pointer. | 
 |     /// This will do things like convert thin pointers to wide | 
 |     /// pointers, or convert structs containing thin pointers to | 
 |     /// structs containing wide pointers, or convert between wide | 
 |     /// pointers. | 
 |     Unsize, | 
 | } | 
 |  | 
 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum CastKind { | 
 |     // FIXME(smir-rename): rename this to PointerExposeProvenance | 
 |     PointerExposeAddress, | 
 |     PointerWithExposedProvenance, | 
 |     PointerCoercion(PointerCoercion), | 
 |     // FIXME(smir-rename): change this to PointerCoercion(DynStar) | 
 |     DynStar, | 
 |     IntToInt, | 
 |     FloatToInt, | 
 |     FloatToFloat, | 
 |     IntToFloat, | 
 |     PtrToPtr, | 
 |     FnPtrToPtr, | 
 |     Transmute, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug, Eq, PartialEq, Serialize)] | 
 | pub enum NullOp { | 
 |     /// Returns the size of a value of that type. | 
 |     SizeOf, | 
 |     /// Returns the minimum alignment of a type. | 
 |     AlignOf, | 
 |     /// Returns the offset of a field. | 
 |     OffsetOf(Vec<(VariantIdx, FieldIdx)>), | 
 |     /// cfg!(ub_checks), but at codegen time | 
 |     UbChecks, | 
 |     /// cfg!(contract_checks), but at codegen time | 
 |     ContractChecks, | 
 | } | 
 |  | 
 | impl Operand { | 
 |     /// Get the type of an operand relative to the local declaration. | 
 |     /// | 
 |     /// In order to retrieve the correct type, the `locals` argument must match the list of all | 
 |     /// locals from the function body where this operand originates from. | 
 |     /// | 
 |     /// Errors indicate a malformed operand or incompatible locals list. | 
 |     pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> { | 
 |         match self { | 
 |             Operand::Copy(place) | Operand::Move(place) => place.ty(locals), | 
 |             Operand::Constant(c) => Ok(c.ty()), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | impl ConstOperand { | 
 |     pub fn ty(&self) -> Ty { | 
 |         self.const_.ty() | 
 |     } | 
 | } | 
 |  | 
 | impl Place { | 
 |     /// Resolve down the chain of projections to get the type referenced at the end of it. | 
 |     /// E.g.: | 
 |     /// Calling `ty()` on `var.field` should return the type of `field`. | 
 |     /// | 
 |     /// In order to retrieve the correct type, the `locals` argument must match the list of all | 
 |     /// locals from the function body where this place originates from. | 
 |     pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> { | 
 |         let start_ty = locals[self.local].ty; | 
 |         self.projection.iter().fold(Ok(start_ty), |place_ty, elem| elem.ty(place_ty?)) | 
 |     } | 
 | } | 
 |  | 
 | impl ProjectionElem { | 
 |     /// Get the expected type after applying this projection to a given place type. | 
 |     pub fn ty(&self, place_ty: Ty) -> Result<Ty, Error> { | 
 |         let ty = place_ty; | 
 |         match &self { | 
 |             ProjectionElem::Deref => Self::deref_ty(ty), | 
 |             ProjectionElem::Field(_idx, fty) => Ok(*fty), | 
 |             ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty), | 
 |             ProjectionElem::Subslice { from, to, from_end } => { | 
 |                 Self::subslice_ty(ty, *from, *to, *from_end) | 
 |             } | 
 |             ProjectionElem::Downcast(_) => Ok(ty), | 
 |             ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), | 
 |         } | 
 |     } | 
 |  | 
 |     fn index_ty(ty: Ty) -> Result<Ty, Error> { | 
 |         ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}")) | 
 |     } | 
 |  | 
 |     fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result<Ty, Error> { | 
 |         let ty_kind = ty.kind(); | 
 |         match ty_kind { | 
 |             TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty), | 
 |             TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array( | 
 |                 inner, | 
 |                 to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?, | 
 |             ), | 
 |             TyKind::RigidTy(RigidTy::Array(inner, size)) => { | 
 |                 let size = size.eval_target_usize()?; | 
 |                 let len = size - from - to; | 
 |                 Ty::try_new_array(inner, len) | 
 |             } | 
 |             _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))), | 
 |         } | 
 |     } | 
 |  | 
 |     fn deref_ty(ty: Ty) -> Result<Ty, Error> { | 
 |         let deref_ty = ty | 
 |             .kind() | 
 |             .builtin_deref(true) | 
 |             .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?; | 
 |         Ok(deref_ty.ty) | 
 |     } | 
 | } |