2121//! #[custom_mir(dialect = "built")]
2222//! pub fn simple(x: i32) -> i32 {
2323//! mir!(
24- //! let temp1: i32;
25- //! let temp2: _;
24+ //! let temp2: i32;
2625//!
2726//! {
28- //! temp1 = x;
27+ //! let temp1 = x;
2928//! Goto(exit)
3029//! }
3130//!
3837//! }
3938//! ```
4039//!
41- //! Hopefully most of this is fairly self-explanatory. Expanding on some notable details:
40+ //! Hopefully the syntax is fairly self-explanatory to anyone familiar with MIR. The `custom_mir`
41+ //! attribute tells the compiler to treat the function as being custom MIR. This attribute only
42+ //! works on functions - there is no way to insert custom MIR into the middle of another function.
43+ //! The `dialect` and `phase` parameters indicate which version of MIR you are inserting here. This
44+ //! will normally be the phase that corresponds to the thing you are trying to test. The phase can
45+ //! be omitted for dialects that have just one.
4246//!
43- //! - The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This
44- //! attribute only works on functions - there is no way to insert custom MIR into the middle of
45- //! another function.
46- //! - The `dialect` and `phase` parameters indicate which version of MIR you are inserting here.
47- //! This will normally be the phase that corresponds to the thing you are trying to test. The
48- //! phase can be omitted for dialects that have just one.
49- //! - You should define your function signature like you normally would. Externally, this function
50- //! can be called like any other function.
51- //! - Type inference works - you don't have to spell out the type of all of your locals.
47+ //! The input to the [`mir!`] macro is:
5248//!
53- //! For now, all statements and terminators are parsed from nested invocations of the special
54- //! functions provided in this module. We additionally want to (but do not yet) support more
55- //! "normal" Rust syntax in places where it makes sense. Also, most kinds of instructions are not
56- //! supported yet.
49+ //! - A possibly empty list of local declarations. Locals can also be declared inline on
50+ //! assignments via `let`. Type inference generally works. Shadowing does not.
51+ //! - A list of basic blocks. The first of these is the start block and is where execution begins.
52+ //! All blocks other than the start block need to be given a name, so that they can be referred
53+ //! to later.
54+ //! - Each block is a list of semicolon terminated statements, followed by a terminator. The
55+ //! syntax for the various statements and terminators is designed to be as similar as possible
56+ //! to the syntax for analogous concepts in native Rust. See below for a list.
57+ //!
58+ //! # Examples
59+ //!
60+ //! ```rust
61+ //! #![feature(core_intrinsics, custom_mir)]
62+ //!
63+ //! extern crate core;
64+ //! use core::intrinsics::mir::*;
65+ //!
66+ //! #[custom_mir(dialect = "built")]
67+ //! pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 {
68+ //! mir!(
69+ //! {
70+ //! match c {
71+ //! true => t,
72+ //! _ => f,
73+ //! }
74+ //! }
75+ //!
76+ //! t = {
77+ //! let temp = a;
78+ //! Goto(load_and_exit)
79+ //! }
80+ //!
81+ //! f = {
82+ //! temp = b;
83+ //! Goto(load_and_exit)
84+ //! }
85+ //!
86+ //! load_and_exit = {
87+ //! RET = *temp;
88+ //! Return()
89+ //! }
90+ //! )
91+ //! }
92+ //!
93+ //! #[custom_mir(dialect = "built")]
94+ //! fn unwrap_unchecked<T>(opt: Option<T>) -> T {
95+ //! mir!({
96+ //! RET = Move(Field(Variant(opt, 1), 0));
97+ //! Return()
98+ //! })
99+ //! }
100+ //! ```
101+ //!
102+ //! We can also set off compilation failures that happen in sufficiently late stages of the
103+ //! compiler:
104+ //!
105+ //! ```rust,compile_fail
106+ //! #![feature(core_intrinsics, custom_mir)]
107+ //!
108+ //! extern crate core;
109+ //! use core::intrinsics::mir::*;
110+ //!
111+ //! #[custom_mir(dialect = "built")]
112+ //! fn borrow_error(should_init: bool) -> i32 {
113+ //! mir!(
114+ //! let temp: i32;
115+ //!
116+ //! {
117+ //! match should_init {
118+ //! true => init,
119+ //! _ => use_temp,
120+ //! }
121+ //! }
122+ //!
123+ //! init = {
124+ //! temp = 0;
125+ //! Goto(use_temp)
126+ //! }
127+ //!
128+ //! use_temp = {
129+ //! RET = temp;
130+ //! Return()
131+ //! }
132+ //! )
133+ //! }
134+ //! ```
135+ //!
136+ //! ```text
137+ //! error[E0381]: used binding is possibly-uninitialized
138+ //! --> test.rs:24:13
139+ //! |
140+ //! 8 | / mir!(
141+ //! 9 | | let temp: i32;
142+ //! 10 | |
143+ //! 11 | | {
144+ //! ... |
145+ //! 19 | | temp = 0;
146+ //! | | -------- binding initialized here in some conditions
147+ //! ... |
148+ //! 24 | | RET = temp;
149+ //! | | ^^^^^^^^^^ value used here but it is possibly-uninitialized
150+ //! 25 | | Return()
151+ //! 26 | | }
152+ //! 27 | | )
153+ //! | |_____- binding declared here but left uninitialized
154+ //!
155+ //! error: aborting due to previous error
156+ //!
157+ //! For more information about this error, try `rustc --explain E0381`.
158+ //! ```
159+ //!
160+ //! # Syntax
161+ //!
162+ //! The lists below are an exahustive description of how various MIR constructs can be created.
163+ //! Anything missing from the list should be assumed to not be supported, PRs welcome.
164+ //!
165+ //! #### Locals
166+ //!
167+ //! - The `_0` return local can always be accessed via `RET`.
168+ //! - Arguments can be accessed via their regular name.
169+ //! - All other locals need to be declared with `let` somewhere and then can be accessed by name.
170+ //!
171+ //! #### Places
172+ //! - Locals implicit convert to places.
173+ //! - Field accesses, derefs, and indexing work normally.
174+ //! - Fields in variants can be accessed via the [`Variant`] and [`Field`] methods, see their
175+ //! documentation for details.
176+ //!
177+ //! #### Operands
178+ //! - Places implicitly convert to `Copy` operands.
179+ //! - `Move` operands can be created via [`Move`].
180+ //! - Const blocks, literals, named constants, and const params all just work.
181+ //! - [`Static`] and [`StaticMut`] can be used to create `&T` and `*mut T`s to statics. These are
182+ //! constants in MIR and the only way to access statics.
183+ //!
184+ //! #### Statements
185+ //! - Assign statements work via normal Rust assignment.
186+ //! - [`Retag`] statements have an associated function.
187+ //!
188+ //! #### Rvalues
189+ //!
190+ //! - Operands implicitly convert to `Use` rvalues.
191+ //! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue.
192+ //! - [`Discriminant`] has an associated function.
193+ //!
194+ //! #### Terminators
195+ //!
196+ //! - [`Goto`] and [`Return`] have associated functions.
197+ //! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
198+ //! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
199+ //! otherwise branch.
57200//!
58201
59202#![ unstable(
69212pub struct BasicBlock ;
70213
71214macro_rules! define {
72- ( $name: literal, $( $sig: tt) * ) => {
215+ ( $name: literal, $( # [ $meta : meta ] ) * fn $ ( $sig: tt) * ) => {
73216 #[ rustc_diagnostic_item = $name]
74- pub $( $sig) * { panic!( ) }
217+ $( #[ $meta ] ) *
218+ pub fn $( $sig) * { panic!( ) }
75219 }
76220}
77221
@@ -88,11 +232,67 @@ define!(
88232 fn Discriminant <T >( place: T ) -> <T as :: core:: marker:: DiscriminantKind >:: Discriminant
89233) ;
90234define ! ( "mir_set_discriminant" , fn SetDiscriminant <T >( place: T , index: u32 ) ) ;
91- define ! ( "mir_field" , fn Field <F >( place: ( ) , field: u32 ) -> F ) ;
92- define ! ( "mir_variant" , fn Variant <T >( place: T , index: u32 ) -> ( ) ) ;
93- define ! ( "mir_make_place" , fn __internal_make_place<T >( place: T ) -> * mut T ) ;
235+ define ! (
236+ "mir_field" ,
237+ /// Access the field with the given index of some place.
238+ ///
239+ /// This only makes sense to use in conjunction with [`Variant`]. If the type you are looking to
240+ /// access the field of does not have variants, you can use normal field projection syntax.
241+ ///
242+ /// There is no proper way to do a place projection to a variant in Rust, and so these two
243+ /// functions are a workaround. You can access a field of a variant via `Field(Variant(place,
244+ /// var_idx), field_idx)`, where `var_idx` and `field_idx` are appropriate literals. Some
245+ /// caveats:
246+ ///
247+ /// - The return type of `Variant` is always `()`. Don't worry about that, the correct MIR will
248+ /// still be generated.
249+ /// - In some situations, the return type of `Field` cannot be inferred. You may need to
250+ /// annotate it on the function in these cases.
251+ /// - Since `Field` is a function call which is not a place expression, using this on the left
252+ /// hand side of an expression is rejected by the compiler. [`place!`] is a macro provided to
253+ /// work around that issue. Wrap the left hand side of an assignment in the macro to convince
254+ /// the compiler that it's ok.
255+ ///
256+ /// # Examples
257+ ///
258+ /// ```rust
259+ /// #![feature(custom_mir, core_intrinsics)]
260+ ///
261+ /// extern crate core;
262+ /// use core::intrinsics::mir::*;
263+ ///
264+ /// #[custom_mir(dialect = "built")]
265+ /// fn unwrap_deref(opt: Option<&i32>) -> i32 {
266+ /// mir!({
267+ /// RET = *Field::<&i32>(Variant(opt, 1), 0);
268+ /// Return()
269+ /// })
270+ /// }
271+ ///
272+ /// #[custom_mir(dialect = "built")]
273+ /// fn set(opt: &mut Option<i32>) {
274+ /// mir!({
275+ /// place!(Field(Variant(*opt, 1), 0)) = 5;
276+ /// Return()
277+ /// })
278+ /// }
279+ /// ```
280+ fn Field <F >( place: ( ) , field: u32 ) -> F
281+ ) ;
282+ define ! (
283+ "mir_variant" ,
284+ /// Adds a variant projection with the given index to the place.
285+ ///
286+ /// See [`Field`] for documentation.
287+ fn Variant <T >( place: T , index: u32 ) -> ( )
288+ ) ;
289+ define ! (
290+ "mir_make_place" ,
291+ #[ doc( hidden) ]
292+ fn __internal_make_place<T >( place: T ) -> * mut T
293+ ) ;
94294
95- /// Convenience macro for generating custom MIR.
295+ /// Macro for generating custom MIR.
96296///
97297/// See the module documentation for syntax details. This macro is not magic - it only transforms
98298/// your MIR into something that is easier to parse in the compiler.
@@ -150,19 +350,7 @@ pub macro mir {
150350
151351/// Helper macro that allows you to treat a value expression like a place expression.
152352///
153- /// This is necessary in combination with the [`Field`] and [`Variant`] methods. Specifically,
154- /// something like this won't compile on its own, reporting an error about not being able to assign
155- /// to such an expression:
156- ///
157- /// ```rust,ignore(syntax-highlighting-only)
158- /// Field(something, 0) = 5;
159- /// ```
160- ///
161- /// Instead, you'll need to write
162- ///
163- /// ```rust,ignore(syntax-highlighting-only)
164- /// place!(Field(something, 0)) = 5;
165- /// ```
353+ /// See the documentation on [`Variant`] for why this is necessary and how to use it.
166354 pub macro place( $e: expr) {
167355 ( * :: core:: intrinsics:: mir:: __internal_make_place ( $e) )
168356}
0 commit comments