Struct flowistry_pdg::rustc::mir::LocalDecl
source · 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`.