Module mir

Rustc internal tooling for hand-writing MIR.

If for some reasons you are not writing rustc tests and have found yourself considering using this feature, turn back. This is exceptionally unstable. There is no attempt at all to make anything work besides those things which the rustc test suite happened to need. If you make a typo you'll probably ICE. Really, this is not the solution to your problems. Consider instead supporting the stable MIR project group.

The documentation for this module describes how to use this feature. If you are interested in hacking on the implementation, most of that documentation lives at rustc_mir_build/src/build/custom/mod.rs.

Typical usage will look like this:

#![feature(core_intrinsics, custom_mir)]
#![allow(internal_features)]

use core::intrinsics::mir::*;

#[custom_mir(dialect = "built")]
pub fn simple(x: i32) -> i32 {
    mir! {
        let temp2: i32;

        {
            let temp1 = x;
            Goto(my_second_block)
        }

        my_second_block = {
            temp2 = Move(temp1);
            RET = temp2;
            Return()
        }
    }
}

The custom_mir attribute tells the compiler to treat the function as being custom MIR. This attribute only works on functions - there is no way to insert custom MIR into the middle of another function. The dialect and phase parameters indicate which version of MIR you are inserting here. Generally you'll want to use #![custom_mir(dialect = "built")] if you want your MIR to be modified by the full MIR pipeline, or #![custom_mir(dialect = "runtime", phase = "optimized")] if you don't.

The input to the [mir!] macro is:

Examples

#![feature(core_intrinsics, custom_mir)]
#![allow(internal_features)]
#![allow(unused_assignments)]

use core::intrinsics::mir::*;

#[custom_mir(dialect = "built")]
pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 {
    mir! {
        {
            match c {
                true => t,
                _ => f,
            }
        }

        t = {
            let temp = a;
            Goto(load_and_exit)
        }

        f = {
            temp = b;
            Goto(load_and_exit)
        }

        load_and_exit = {
            RET = *temp;
            Return()
        }
    }
}

#[custom_mir(dialect = "built")]
fn unwrap_unchecked<T>(opt: Option<T>) -> T {
    mir! {
        {
            RET = Move(Field(Variant(opt, 1), 0));
            Return()
        }
    }
}

#[custom_mir(dialect = "runtime", phase = "optimized")]
fn push_and_pop<T>(v: &mut Vec<T>, value: T) {
    mir! {
        let _unused;
        let popped;

        {
            Call(_unused = Vec::push(v, value), ReturnTo(pop), UnwindContinue())
        }

        pop = {
            Call(popped = Vec::pop(v), ReturnTo(drop), UnwindContinue())
        }

        drop = {
            Drop(popped, ReturnTo(ret), UnwindContinue())
        }

        ret = {
            Return()
        }
    }
}

#[custom_mir(dialect = "runtime", phase = "optimized")]
fn annotated_return_type() -> (i32, bool) {
    mir! {
        type RET = (i32, bool);
        {
            RET.0 = 1;
            RET.1 = true;
            Return()
        }
    }
}

We can also set off compilation failures that happen in sufficiently late stages of the compiler:

#![feature(core_intrinsics, custom_mir)]

extern crate core;
use core::intrinsics::mir::*;

#[custom_mir(dialect = "built")]
fn borrow_error(should_init: bool) -> i32 {
    mir! {
        let temp: i32;

        {
            match should_init {
                true => init,
                _ => use_temp,
            }
        }

        init = {
            temp = 0;
            Goto(use_temp)
        }

        use_temp = {
            RET = temp;
            Return()
        }
    }
}
error[E0381]: used binding is possibly-uninitialized
  --> test.rs:24:13
   |
8  | /     mir! {
9  | |         let temp: i32;
10 | |
11 | |         {
...  |
19 | |             temp = 0;
   | |             -------- binding initialized here in some conditions
...  |
24 | |             RET = temp;
   | |             ^^^^^^^^^^ value used here but it is possibly-uninitialized
25 | |             Return()
26 | |         }
27 | |     }
   | |_____- binding declared here but left uninitialized

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0381`.

Syntax

The lists below are an exhaustive description of how various MIR constructs can be created. Anything missing from the list should be assumed to not be supported, PRs welcome.

Locals

Places

Operands

Statements

Rvalues

Terminators

Debuginfo

Debuginfo associates source code variable names (of variables that may not exist any more) with MIR expressions that indicate where the value of that variable is stored. The syntax to do so is:

debug source_var_name => expression;

Both places and constants are supported in the expression.

#![allow(internal_features)]
#![feature(core_intrinsics, custom_mir)]

use core::intrinsics::mir::*;

#[custom_mir(dialect = "built")]
fn debuginfo(arg: Option<&i32>) {
    mir!(
        // Debuginfo for a source variable `plain_local` that just duplicates `arg`.
        debug plain_local => arg;
        // Debuginfo for a source variable `projection` that can be computed by dereferencing
        // a field of `arg`.
        debug projection => *Field::<&i32>(Variant(arg, 1), 0);
        // Debuginfo for a source variable `constant` that always holds the value `5`.
        debug constant => 5_usize;
        {
            Return()
        }
    )
}

Structs

Enums

Functions

Macros