Skip to content

Commit 60ee62d

Browse files
committed
Support arbitrary let statements in custom mir
1 parent 2efe92f commit 60ee62d

File tree

1 file changed

+139
-4
lines changed

1 file changed

+139
-4
lines changed

core/src/intrinsics/mir.rs

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,14 @@ pub macro mir {
9090
(
9191
$(let $local_decl:ident $(: $local_decl_ty:ty)? ;)*
9292

93-
$entry_block:block
93+
{
94+
$($entry:tt)*
95+
}
9496

9597
$(
96-
$block_name:ident = $block:block
98+
$block_name:ident = {
99+
$($block:tt)*
100+
}
97101
)*
98102
) => {{
99103
// First, we declare all basic blocks.
@@ -109,15 +113,146 @@ pub macro mir {
109113
let $local_decl $(: $local_decl_ty)? ;
110114
)*
111115

116+
::core::intrinsics::mir::__internal_extract_let!($($entry)*);
117+
$(
118+
::core::intrinsics::mir::__internal_extract_let!($($block)*);
119+
)*
120+
112121
{
113122
// Finally, the contents of the basic blocks
114-
$entry_block;
123+
::core::intrinsics::mir::__internal_remove_let!({
124+
{}
125+
{ $($entry)* }
126+
});
115127
$(
116-
$block;
128+
::core::intrinsics::mir::__internal_remove_let!({
129+
{}
130+
{ $($block)* }
131+
});
117132
)*
118133

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

0 commit comments

Comments
 (0)