Skip to content

Incorrect MIR printed with --emit=mir when a closure captures multiple fields of a single value. #154166

Description

@theemathas

I generated the MIR of the following code:

pub fn foo(x: (String, String)) {
    let _closure = || {
        if std::hint::black_box(true) {
            let _a = &x.1;
        } else {
            let _b = x.0;
        }
    };
}

I got the following MIR:

MIR
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
// HINT: See also -Z dump-mir for MIR at specific points during compilation.
fn foo(_1: (String, String)) -> () {
    debug x => _1;
    let mut _0: ();
    let _2: {closure@src/lib.rs:2:20: 2:22};
    let mut _3: &std::string::String;
    scope 1 {
        debug _closure => _2;
    }

    bb0: {
        _3 = &(_1.1: std::string::String);
        _2 = {closure@src/lib.rs:2:20: 2:22} { x: move (_1.0: std::string::String) };
        drop(_2) -> [return: bb1, unwind: bb4];
    }

    bb1: {
        drop((_1.1: std::string::String)) -> [return: bb3, unwind continue];
    }

    bb2 (cleanup): {
        resume;
    }

    bb3: {
        return;
    }

    bb4 (cleanup): {
        drop((_1.1: std::string::String)) -> [return: bb2, unwind terminate(cleanup)];
    }
}

fn foo::{closure#0}(_1: {closure@src/lib.rs:2:20: 2:22}) -> () {
    debug x__0 => (_1.0: std::string::String);
    debug x__1 => (*(_1.1: &std::string::String));
    let mut _0: ();
    let mut _2: bool;
    let _3: std::string::String;
    let mut _4: &std::string::String;
    let mut _5: bool;
    scope 1 {
        debug _a => _4;
    }
    scope 2 {
        debug _b => _3;
    }

    bb0: {
        _5 = const false;
        _5 = const true;
        _2 = std::hint::black_box::<bool>(const true) -> [return: bb1, unwind: bb9];
    }

    bb1: {
        switchInt(move _2) -> [0: bb3, otherwise: bb2];
    }

    bb2: {
        _4 = copy (_1.1: &std::string::String);
        goto -> bb4;
    }

    bb3: {
        _5 = const false;
        _3 = move (_1.0: std::string::String);
        drop(_3) -> [return: bb4, unwind: bb9];
    }

    bb4: {
        switchInt(copy _5) -> [0: bb6, otherwise: bb7];
    }

    bb5 (cleanup): {
        resume;
    }

    bb6: {
        return;
    }

    bb7: {
        drop((_1.0: std::string::String)) -> [return: bb6, unwind continue];
    }

    bb8 (cleanup): {
        drop((_1.0: std::string::String)) -> [return: bb5, unwind terminate(cleanup)];
    }

    bb9 (cleanup): {
        switchInt(copy _5) -> [0: bb5, otherwise: bb8];
    }
}

Notably, here's the code that constructs the closure:

_3 = &(_1.1: std::string::String);
_2 = {closure@src/lib.rs:2:20: 2:22} { x: move (_1.0: std::string::String) };
drop(_2) -> [return: bb1, unwind: bb4];

I think that the MIR printing is printing only the closure capture for one of the fields, instead of both.

Meta

Reproducible on the playground with the "show MIR" option with version 1.96.0-nightly (2026-03-12 3102493c71626b5912d1)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.htmlA-closuresArea: Closures (`|…| { … }`)C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions