Skip to content

NLL: refine liveness with "maybe initialized" analysis #45665

Closed
@nikomatsakis

Description

@nikomatsakis

As part of #45538, we are incorporating knowledge of which variables are live (i.e., may be used later). We also distinguish "drop-live", which means that they may be dropped later.

However, in MIR, we sometimes generate drops that we can see statically will be a no-op. For example, consider this program:

// compile-flags:-Znll
fn main() {
    let mut v = 1;
    let p = Wrap { value: &v }; // freezes `v` so long as `p` is in use
    ignore(p); // moves `p`
    v += 1; // Errors, even in NLL! (At least as implemented now.)
} // <-- p is dropped here, but we **know** this to be a no-op

fn ignore<T>(x: T) { }

struct Wrap<T> { value: T }

impl<T> Drop for Wrap<T> {
    fn drop(&mut self) { }
}

The problem is that p is considered "drop-live" at the point where v += 1, because there is a DROP in the MIR, even though we know that p must have been moved (and hence will not be dropped) at that point.

One way to account for this is to do a maybe initialized analysis. This is a forwards analysis that tells you, at any given point, whether a given value could possibly be initialized that this point. The idea then would be to change the code which invokes the add_drop_live_constraint function to take the "maybe initialized" state into account. In particular, if the local variable that is being dropped cannot be initialized (i.e., maybe initialized is false), then we can ignore the fact that it is drop live and just not invoke add_drop_live_constraint with its type.

We are already computing a "maybe initialized" in the borrow checker (the variable flow_inits). However, this analysis works a bit differently than the NLL code. For example, it is computed at the level of fragments, which are paths like a.b.c. This is done because it is possible that a was initialized, but some subpart of it (e.g., a.b.c) was moved, while the rest remains intact.

Honestly, all the data structures are a bit out of cache for me, but the high-level idea then is that, when we know that a variable X is drop-live, we want to iterate over all of the subpaths of a that may be initialized (which the existing data structures should be able to tell us) and then invoke the existing add_drop_live_constraint function on the types of those fragments.

cc @arielb1 @pnkfelix -- any notes you care to leave on how to find the maybe initialized fragments given a mir::Local would be appreciated. Else I will dig into the data structures at some point.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions