21
21
//! #[custom_mir(dialect = "built")]
22
22
//! pub fn simple(x: i32) -> i32 {
23
23
//! mir!(
24
- //! let temp1: i32;
25
- //! let temp2: _;
24
+ //! let temp2: i32;
26
25
//!
27
26
//! {
28
- //! temp1 = x;
27
+ //! let temp1 = x;
29
28
//! Goto(exit)
30
29
//! }
31
30
//!
38
37
//! }
39
38
//! ```
40
39
//!
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.
42
46
//!
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:
52
48
//!
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.
57
200
//!
58
201
59
202
#![ unstable(
69
212
pub struct BasicBlock ;
70
213
71
214
macro_rules! define {
72
- ( $name: literal, $( $sig: tt) * ) => {
215
+ ( $name: literal, $( # [ $meta : meta ] ) * fn $ ( $sig: tt) * ) => {
73
216
#[ rustc_diagnostic_item = $name]
74
- pub $( $sig) * { panic!( ) }
217
+ $( #[ $meta ] ) *
218
+ pub fn $( $sig) * { panic!( ) }
75
219
}
76
220
}
77
221
@@ -88,11 +232,67 @@ define!(
88
232
fn Discriminant <T >( place: T ) -> <T as :: core:: marker:: DiscriminantKind >:: Discriminant
89
233
) ;
90
234
define ! ( "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
+ ) ;
94
294
95
- /// Convenience macro for generating custom MIR.
295
+ /// Macro for generating custom MIR.
96
296
///
97
297
/// See the module documentation for syntax details. This macro is not magic - it only transforms
98
298
/// your MIR into something that is easier to parse in the compiler.
@@ -150,19 +350,7 @@ pub macro mir {
150
350
151
351
/// Helper macro that allows you to treat a value expression like a place expression.
152
352
///
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.
166
354
pub macro place( $e: expr) {
167
355
( * :: core:: intrinsics:: mir:: __internal_make_place ( $e) )
168
356
}
0 commit comments