pub struct LocalDecl<'tcx> {
pub mutability: Mutability,
pub local_info: ClearCrossCrate<Box<LocalInfo<'tcx>>>,
pub ty: Ty<'tcx>,
pub user_ty: Option<Box<UserTypeProjections>>,
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>>>
§ty: Ty<'tcx>
The type of this local.
user_ty: Option<Box<UserTypeProjections>>
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`.