pub struct LocalDecl<'tcx> {
    pub mutability: Mutability,
    pub local_info: ClearCrossCrate<Box<LocalInfo<'tcx>, Global>>,
    pub internal: bool,
    pub ty: Ty<'tcx>,
    pub user_ty: Option<Box<UserTypeProjections, Global>>,
    pub source_info: SourceInfo,
}
Expand description

A MIR local.

This can be a binding declared by the user, a temporary inserted by the compiler, a function argument, or the return place.

Fields§

§mutability: Mutability

Whether this is a mutable binding (i.e., let x or let mut x).

Temporaries and the return place are always mutable.

§local_info: ClearCrossCrate<Box<LocalInfo<'tcx>, Global>>§internal: bool

true if this is an internal local.

These locals are not based on types in the source code and are only used for a few desugarings at the moment.

The generator transformation will sanity check the locals which are live across a suspension point against the type components of the generator which type checking knows are live across a suspension point. We need to flag drop flags to avoid triggering this check as they are introduced outside of type inference.

This should be sound because the drop flags are fully algebraic, and therefore don’t affect the auto-trait or outlives properties of the generator.

§ty: Ty<'tcx>

The type of this local.

§user_ty: Option<Box<UserTypeProjections, Global>>

If the user manually ascribed a type to this variable, e.g., via let x: T, then we carry that type here. The MIR borrow checker needs this information since it can affect region inference.

§source_info: SourceInfo

The syntactic (i.e., not visibility) source scope the local is defined in. If the local was defined in a let-statement, this is within the let-statement, rather than outside of it.

This is needed because the visibility source scope of locals within a let-statement is weird.

The reason is that we want the local to be within the let-statement for lint purposes, but we want the local to be after the let-statement for names-in-scope purposes.

That’s it, if we have a let-statement like the one in this function:

fn foo(x: &str) {
    #[allow(unused_mut)]
    let mut x: u32 = { // <- one unused mut
        let mut y: u32 = x.parse().unwrap();
        y + 2
    };
    drop(x);
}

Then, from a lint point of view, the declaration of x: u32 (and y: u32) are within the #[allow(unused_mut)] scope - the lint scopes are the same as the AST/HIR nesting.

However, from a name lookup point of view, the scopes look more like as if the let-statements were match expressions:

fn foo(x: &str) {
    match {
        match x.parse::<u32>().unwrap() {
            y => y + 2
        }
    } {
        x => drop(x)
    };
}

We care about the name-lookup scopes for debuginfo - if the debuginfo instruction pointer is at the call to x.parse(), we want x to refer to x: &str, but if it is at the call to drop(x), we want it to refer to x: u32.

To allow both uses to work, we need to have more than a single scope for a local. We have the source_info.scope represent the “syntactic” lint scope (with a variable being under its let block) while the var_debug_info.source_info.scope represents the “local variable” scope (where the “rest” of a block is under all prior let-statements).

The end result looks like this:

ROOT SCOPE
 │{ argument x: &str }
 │
 │ │{ #[allow(unused_mut)] } // This is actually split into 2 scopes
 │ │                         // in practice because I'm lazy.
 │ │
 │ │← x.source_info.scope
 │ │← `x.parse().unwrap()`
 │ │
 │ │ │← y.source_info.scope
 │ │
 │ │ │{ let y: u32 }
 │ │ │
 │ │ │← y.var_debug_info.source_info.scope
 │ │ │← `y + 2`
 │
 │ │{ let x: u32 }
 │ │← x.var_debug_info.source_info.scope
 │ │← `drop(x)` // This accesses `x: u32`.

Auto Trait Implementations§

§

impl<'tcx> !RefUnwindSafe for LocalDecl<'tcx>

§

impl<'tcx> !Send for LocalDecl<'tcx>

§

impl<'tcx> !Sync for LocalDecl<'tcx>

§

impl<'tcx> Unpin for LocalDecl<'tcx>

§

impl<'tcx> !UnwindSafe for LocalDecl<'tcx>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.