Skip to content

Commit d3f1a85

Browse files
committed
Auto merge of rust-lang#104975 - JakobDegen:custom_mir_let, r=oli-obk
`#![custom_mir]`: Various improvements This PR makes a bunch of improvements to `#![custom_mir]`. Ideally this would be 4 PRs, one for each commit, but those would take forever to get merged and be a pain to juggle. Should still be reviewed one commit at a time though. ### Commit 1: Support arbitrary `let` Before this change, all locals used in the body need to be declared at the top of the `mir!` invocation, which is rather annoying. We attempt to change that. Unfortunately, we still have the requirement that the output of the `mir!` macro must resolve, typecheck, etc. Because of that, we can't just accept this in the THIR -> MIR parser because something like ```rust { let x = 0; Goto(other) } other = { RET = x; Return() } ``` will fail to resolve. Instead, the implementation does macro shenanigans to find the let declarations and extract them as part of the `mir!` macro. That *works*, but it is fairly complicated and degrades debuginfo by quite a bit. Specifically, the spans for any statements and declarations that are affected by this are completely wrong. My guess is that this is a net improvement though. One way to recover some of the debuginfo would be to not support type annotations in the `let` statements, which would allow us to parse like `let $stmt:stmt`. That seems quite surprising though. ### Commit 2: Parse consts Reuses most of the const parsing from regular Mir building for building custom mir ### Commit 3: Parse statics Statics are slightly weird because the Mir primitive associated with them is a reference/pointer to them, so this is factored out separately. ### Commit 4: Fix some spans A bunch of the spans were non-ideal, so we adjust them to be much more helpful. r? `@oli-obk`
2 parents cf6847c + 92dead6 commit d3f1a85

File tree

1 file changed

+170
-4
lines changed

1 file changed

+170
-4
lines changed

core/src/intrinsics/mir.rs

Lines changed: 170 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
8080
define!("mir_retag", fn Retag<T>(place: T));
8181
define!("mir_retag_raw", fn RetagRaw<T>(place: T));
8282
define!("mir_move", fn Move<T>(place: T) -> T);
83+
define!("mir_static", fn Static<T>(s: T) -> &'static T);
84+
define!("mir_static_mut", fn StaticMut<T>(s: T) -> *mut T);
8385

8486
/// Convenience macro for generating custom MIR.
8587
///
@@ -90,10 +92,14 @@ pub macro mir {
9092
(
9193
$(let $local_decl:ident $(: $local_decl_ty:ty)? ;)*
9294

93-
$entry_block:block
95+
{
96+
$($entry:tt)*
97+
}
9498

9599
$(
96-
$block_name:ident = $block:block
100+
$block_name:ident = {
101+
$($block:tt)*
102+
}
97103
)*
98104
) => {{
99105
// First, we declare all basic blocks.
@@ -109,15 +115,175 @@ pub macro mir {
109115
let $local_decl $(: $local_decl_ty)? ;
110116
)*
111117

118+
::core::intrinsics::mir::__internal_extract_let!($($entry)*);
119+
$(
120+
::core::intrinsics::mir::__internal_extract_let!($($block)*);
121+
)*
122+
112123
{
113124
// Finally, the contents of the basic blocks
114-
$entry_block;
125+
::core::intrinsics::mir::__internal_remove_let!({
126+
{}
127+
{ $($entry)* }
128+
});
115129
$(
116-
$block;
130+
::core::intrinsics::mir::__internal_remove_let!({
131+
{}
132+
{ $($block)* }
133+
});
117134
)*
118135

119136
RET
120137
}
121138
}
122139
}}
123140
}
141+
142+
/// Helper macro that extracts the `let` declarations out of a bunch of statements.
143+
///
144+
/// This macro is written using the "statement muncher" strategy. Each invocation parses the first
145+
/// statement out of the input, does the appropriate thing with it, and then recursively calls the
146+
/// same macro on the remainder of the input.
147+
#[doc(hidden)]
148+
pub macro __internal_extract_let {
149+
// If it's a `let` like statement, keep the `let`
150+
(
151+
let $var:ident $(: $ty:ty)? = $expr:expr; $($rest:tt)*
152+
) => {
153+
let $var $(: $ty)?;
154+
::core::intrinsics::mir::__internal_extract_let!($($rest)*);
155+
},
156+
// Due to #86730, we have to handle const blocks separately
157+
(
158+
let $var:ident $(: $ty:ty)? = const $block:block; $($rest:tt)*
159+
) => {
160+
let $var $(: $ty)?;
161+
::core::intrinsics::mir::__internal_extract_let!($($rest)*);
162+
},
163+
// Otherwise, output nothing
164+
(
165+
$stmt:stmt; $($rest:tt)*
166+
) => {
167+
::core::intrinsics::mir::__internal_extract_let!($($rest)*);
168+
},
169+
(
170+
$expr:expr
171+
) => {}
172+
}
173+
174+
/// Helper macro that removes the `let` declarations from a bunch of statements.
175+
///
176+
/// Because expression position macros cannot expand to statements + expressions, we need to be
177+
/// slightly creative here. The general strategy is also statement munching as above, but the output
178+
/// of the macro is "stored" in the subsequent macro invocation. Easiest understood via example:
179+
/// ```text
180+
/// invoke!(
181+
/// {
182+
/// {
183+
/// x = 5;
184+
/// }
185+
/// {
186+
/// let d = e;
187+
/// Call()
188+
/// }
189+
/// }
190+
/// )
191+
/// ```
192+
/// becomes
193+
/// ```text
194+
/// invoke!(
195+
/// {
196+
/// {
197+
/// x = 5;
198+
/// d = e;
199+
/// }
200+
/// {
201+
/// Call()
202+
/// }
203+
/// }
204+
/// )
205+
/// ```
206+
#[doc(hidden)]
207+
pub macro __internal_remove_let {
208+
// If it's a `let` like statement, remove the `let`
209+
(
210+
{
211+
{
212+
$($already_parsed:tt)*
213+
}
214+
{
215+
let $var:ident $(: $ty:ty)? = $expr:expr;
216+
$($rest:tt)*
217+
}
218+
}
219+
) => { ::core::intrinsics::mir::__internal_remove_let!(
220+
{
221+
{
222+
$($already_parsed)*
223+
$var = $expr;
224+
}
225+
{
226+
$($rest)*
227+
}
228+
}
229+
)},
230+
// Due to #86730 , we have to handle const blocks separately
231+
(
232+
{
233+
{
234+
$($already_parsed:tt)*
235+
}
236+
{
237+
let $var:ident $(: $ty:ty)? = const $block:block;
238+
$($rest:tt)*
239+
}
240+
}
241+
) => { ::core::intrinsics::mir::__internal_remove_let!(
242+
{
243+
{
244+
$($already_parsed)*
245+
$var = const $block;
246+
}
247+
{
248+
$($rest)*
249+
}
250+
}
251+
)},
252+
// Otherwise, keep going
253+
(
254+
{
255+
{
256+
$($already_parsed:tt)*
257+
}
258+
{
259+
$stmt:stmt;
260+
$($rest:tt)*
261+
}
262+
}
263+
) => { ::core::intrinsics::mir::__internal_remove_let!(
264+
{
265+
{
266+
$($already_parsed)*
267+
$stmt;
268+
}
269+
{
270+
$($rest)*
271+
}
272+
}
273+
)},
274+
(
275+
{
276+
{
277+
$($already_parsed:tt)*
278+
}
279+
{
280+
$expr:expr
281+
}
282+
}
283+
) => {
284+
{
285+
$($already_parsed)*
286+
$expr
287+
}
288+
},
289+
}

0 commit comments

Comments
 (0)