How to understand MIR downcast in MIR files

I used -Zdump-mir option for rustc to get MIR files. I have problem understanding this line:

_5 = move ((_2 as Ok).0: std::sync::RwLockReadGuard<i32>); // bb7[2]: scope 1 at src\main.rs:19:12: 19:13

For the part of _5 = move ((_2 as Ok).0, does this means "make _2 itself into Ok, then move it to _5", or "make a copy of _2 then downcast it as Ok then move to _5"?

The difference is, the former implicates that _2 itself is moved to _5, _2 won't exist nor can be used after this line; while the latter means _2 is still alive and can be used later. As I am trying to get lifetime of variables, this makes difference. Does anyone have any ideas?

What is the original code? It can usually be told what is happening without diving into MIR.

On a side note, what problem are you trying to solve? "getting the lifetime of variables" isn't something that should be done manually (In fact you can't) and should be done using generics and lifetime parameters.

Consider the following program:

struct NoCopy;
enum E { N, S { a: NoCopy, b: NoCopy } }

fn foo() {
    let x = E::S { a: NoCopy, b: NoCopy };

    match x {
        E::S { a, b: _ } => {
            a;
        }
        _ => {}
    }
}

with MIR:

fn  foo() -> () {
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:4:10: 4:10
    let mut _2: NoCopy;                  // in scope 0 at src/lib.rs:5:23: 5:29
    let mut _3: NoCopy;                  // in scope 0 at src/lib.rs:5:34: 5:40
    let mut _4: isize;                   // in scope 0 at src/lib.rs:8:9: 8:25
    let mut _6: NoCopy;                  // in scope 0 at src/lib.rs:9:13: 9:14
    scope 1 {
        let _1: E;                       // "x" in scope 1 at src/lib.rs:5:9: 5:10
    }
    scope 2 {
        let _5: NoCopy;                  // "a" in scope 2 at src/lib.rs:8:16: 8:17
        scope 3 {
        }
    }

    bb0: {
        StorageLive(_1);                 // bb0[0]: scope 0 at src/lib.rs:5:9: 5:10
        StorageLive(_2);                 // bb0[1]: scope 0 at src/lib.rs:5:23: 5:29
        StorageLive(_3);                 // bb0[2]: scope 0 at src/lib.rs:5:34: 5:40
        ((_1 as S).0: NoCopy) = move _2; // bb0[3]: scope 0 at src/lib.rs:5:13: 5:42
        ((_1 as S).1: NoCopy) = move _3; // bb0[4]: scope 0 at src/lib.rs:5:13: 5:42
        discriminant(_1) = 1;            // bb0[5]: scope 0 at src/lib.rs:5:13: 5:42
        StorageDead(_3);                 // bb0[6]: scope 0 at src/lib.rs:5:41: 5:42
        StorageDead(_2);                 // bb0[7]: scope 0 at src/lib.rs:5:41: 5:42
        _4 = discriminant(_1);           // bb0[8]: scope 2 at src/lib.rs:8:9: 8:25
        switchInt(move _4) -> [1isize: bb1, otherwise: bb2]; // bb0[9]: scope 2 at src/lib.rs:8:9: 8:25
    }

    bb1: {
        StorageLive(_5);                 // bb1[0]: scope 2 at src/lib.rs:8:16: 8:17
        _5 = move ((_1 as S).0: NoCopy); // bb1[1]: scope 2 at src/lib.rs:8:16: 8:17
        _6 = move _5;                    // bb1[2]: scope 3 at src/lib.rs:9:13: 9:14
        goto -> bb2;                     // bb1[3]: scope 2 at src/lib.rs:7:5: 12:6
    }

    bb2: {
        StorageDead(_5);                 // bb2[0]: scope 2 at src/lib.rs:12:5: 12:6
        StorageDead(_1);                 // bb2[1]: scope 0 at src/lib.rs:13:1: 13:2
        return;                          // bb2[2]: scope 0 at src/lib.rs:13:2: 13:2
    }
}

If we look at bb1 we see: _5 = move ((_1 as S).0: NoCopy);

Having checked previously that the discriminant matches, we now move the 0th field of S (which is a) into _5.

Clearly, since NoCopy isn't Copy we may no longer access _1 (if you try you get an error).

PS: These questions are better suited for https://internals.rust-lang.org/.

2 Likes