Skip to content

Closure field capturing leads to inconsistent MIR printing #115307

Open
@RalfJung

Description

@RalfJung

I am trying to learn what closure field capturing exactly does by studying the dumped MIR for a couple of interesting cases. Unfortunately that MIR contradicts itself:

fn call(f: impl FnOnce()) { f() }

#[derive(Default)]
struct S {
    field0: u8,
    field1: u8,
    field2: u8,
}

fn test() {
    let s = S::default();
    call(|| {
        let _v = &s.field1;
        let _v = &s.field2;
    })
}

prints this for the call site:

    let _1: S;

    bb1: {
        _3 = &(_1.1: u8);
        _4 = &(_1.2: u8);
        _2 = [closure@src/lib.rs:12:10: 12:12] { s: move _3 };
        _0 = call::<[closure@src/lib.rs:12:10: 12:12]>(move _2) -> [return: bb2, unwind continue];
    }

and this for the closure body

fn test::{closure#0}(_1: [closure@src/lib.rs:12:10: 12:12]) -> () {
    let mut _0: ();
    let mut _2: &u8;
    let mut _3: &u8;

    bb0: {
        _2 = deref_copy (_1.0: &u8);
        _3 = deref_copy (_1.1: &u8);
        return;
    }
}

So at the call site it seems like the closure type has a single field called s of type &u8. But in the body it looks like it has 2 fields (MIR as usual only shows their indices, 0 and 1) that each have type &u8. Which one is true?

This test case prints "16" so it looks like there really are two reference-typed fields. That makes the printing at the site where the closure values are being captured rather misleading.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions