1#![cfg_attr(not(test), no_std)]
6
7#![cfg_attr(test, recursion_limit = "1000")]
9
10#![cfg_attr(feature = "nightly", feature(macro_reexport))]
12
13#[cfg(all(test, not(feature = "nightly")))] #[macro_use] #[no_link] extern crate static_cond; #[cfg(feature = "nightly")] #[macro_use] #[no_link] #[macro_reexport(static_cond)] extern crate static_cond; #[macro_export]
70macro_rules! block {
71 (@as_expr $e:expr) => { $e };
82
83 (@error $err:ident) => {{
85 struct $err;
86 let _: () = $err;
87 }};
88
89 (@wrap $life:tt () $ret:ident ($($init:tt)*) $out:expr) => {
98 block!(@as_expr
99 {
100 let $ret $($init)*;
101 $life: loop {
102 $ret = $out;
103 break $life;
104 }
105 $ret
106 })
107 };
108 (@wrap $life:tt (loop) $ret:ident ($($init:tt)*) $out:expr) => {
109 block!(@as_expr
110 {
111 let $ret $($init)*;
112 $life: loop {
113 $out;
114 }
115 $ret
116 })
117 };
118
119 (@scan {} $life:tt $ret:ident () -> ($($out:tt)*) (() $lp:tt $init:tt)) => {
135 block!(@wrap $life $lp $ret $init { $($out)* })
136 };
137 (@scan {} $life:tt $ret:ident () -> ($($out:tt)*) $stack:tt) => {
139 block!(@up $life $ret { $($out)* } $stack)
140 };
141 (@scan () $life:tt $ret:ident () -> ($($out:tt)*) $stack:tt) => {
143 block!(@up $life $ret ( $($out)* ) $stack)
144 };
145 (@scan [] $life:tt $ret:ident () -> ($($out:tt)*) $stack:tt) => {
147 block!(@up $life $ret [ $($out)* ] $stack)
148 };
149
150 (@scan $paren:tt $life:tt $ret:ident (break) -> ($($out:tt)*) $stack:tt) => {
155 block!(@scan $paren $life $ret () -> ($($out)* block!(@error NoBareBreakInNamedBlock);) $stack)
156 };
157 (@scan $paren:tt $life:tt $ret:ident (break; $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
158 block!(@scan $paren $life $ret ($($tail)*) -> ($($out)* block!(@error NoBareBreakInNamedBlock);) $stack)
159 };
160 (@scan $paren:tt $life:tt $ret:ident (continue) -> ($($out:tt)*) $stack:tt) => {
161 block!(@scan $paren $life $ret () -> ($($out)* block!(@error NoBareContinueInNamedBlock);) $stack)
162 };
163 (@scan $paren:tt $life:tt $ret:ident (continue; $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
164 block!(@scan $paren $life $ret ($($tail)*) -> ($($out)* block!(@error NoBareContinueInNamedBlock);) $stack)
165 };
166 (@scan $paren:tt $life1:tt $ret:ident (break $life2:tt; $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
168 block!(@scan $paren $life1 $ret ($($tail)*) -> ($($out)* break $life2;) $stack)
169 };
170 (@scan $paren:tt $life1:tt $ret:ident (break $life2:tt $e:expr; $($tail:tt)*) -> ($($out:tt)*) ($stack:tt $lp:tt $init:tt)) => {
172 static_cond! {
173 if $life1 == $life2 {
174 block!(@scan $paren $life1 $ret ($($tail)*) -> ($($out)* { $ret = $e; break $life2; }) ($stack $lp ()))
175 } else {
176 block!(@scan $paren $life1 $ret ($($tail)*) -> ($($out)* break $life2 $e;) ($stack $lp ()))
177 }
178 }
179 };
180 (@scan $paren:tt $life1:tt $ret:ident (break $life2:tt $e:expr) -> ($($out:tt)*) ($stack:tt $lp:tt $init:tt)) => {
181 static_cond! {
182 if $life1 == $life2 {
183 block!(@scan $paren $life1 $ret () -> ($($out)* { $ret = $e; break $life2 }) ($stack $lp ()))
184 } else {
186 block!(@scan $paren $life1 $ret () -> ($($out)* break $life2 $e;) ($stack $lp ()))
187 }
188 }
189 };
190 (@scan $paren:tt $life1:tt $ret:ident (continue $life2:tt; $($tail:tt)*) -> ($($out:tt)*) ($stack:tt () $init:tt)) => {
193 static_cond! {
194 if $life1 == $life2 {
195 block!(@scan $paren $life1 $ret ($($tail)*) -> ($($out)* block!(@error NoMatchedContinueInNamedBlock);) ($stack () $init))
196 } else {
197 block!(@scan $paren $life1 $ret ($($tail)*) -> ($($out)* continue $life2;) ($stack () $init))
198 }
199 }
200 };
201 (@scan $paren:tt $life1:tt $ret:ident (continue $life2:tt) -> ($($out:tt)*) $stack:tt) => {
202 static_cond! {
203 if $life1 == $life2 {
204 block!(@scan $paren $life1 $ret () -> ($($out)* block!(@error NoMatchedContinueInNamedBlock);) $stack)
205 } else {
206 block!(@scan $paren $life1 $ret () -> ($($out)* continue $life2;) $stack)
207 }
208 }
209 };
210
211 (@scan_item $paren:tt $life:tt $ret:ident ($ignore:item $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
214 block!(@scan $paren $life $ret ($($tail)*) -> ($($out)* $ignore) $stack)
215 };
216
217 (@scan $paren:tt $life:tt $ret:ident (#[block(ignore)] $ignore:tt $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
219 block!(@scan $paren $life $ret ($($tail)*) -> ($($out)* $ignore) $stack)
220 };
221 (@scan $paren:tt $life:tt $ret:ident (#[$attr:meta] $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
223 block!(@scan $paren $life $ret ($($tail)*) -> ($($out)* #[$attr]) $stack)
224 };
225 (@scan $paren:tt $life:tt $ret:ident (pub $($tail:tt)*) -> $out:tt $stack:tt) => {
227 block!(@scan_item $paren $life $ret (pub $($tail)*) -> $out $stack)
228 };
229 (@scan $paren:tt $life:tt $ret:ident (use $($tail:tt)*) -> $out:tt $stack:tt) => {
230 block!(@scan_item $paren $life $ret (use $($tail)*) -> $out $stack)
231 };
232 (@scan $paren:tt $life:tt $ret:ident (extern $($tail:tt)*) -> $out:tt $stack:tt) => {
233 block!(@scan_item $paren $life $ret (extern $($tail)*) -> $out $stack)
234 };
235 (@scan $paren:tt $life:tt $ret:ident (mod $($tail:tt)*) -> $out:tt $stack:tt) => {
236 block!(@scan_item $paren $life $ret (mod $($tail)*) -> $out $stack)
237 };
238 (@scan $paren:tt $life:tt $ret:ident (static $($tail:tt)*) -> $out:tt $stack:tt) => {
239 block!(@scan_item $paren $life $ret (static $($tail)*) -> $out $stack)
240 };
241 (@scan $paren:tt $life:tt $ret:ident (const $($tail:tt)*) -> $out:tt $stack:tt) => {
242 block!(@scan_item $paren $life $ret (const $($tail)*) -> $out $stack)
243 };
244 (@scan $paren:tt $life:tt $ret:ident (trait $($tail:tt)*) -> $out:tt $stack:tt) => {
245 block!(@scan_item $paren $life $ret (trait $($tail)*) -> $out $stack)
246 };
247 (@scan $paren:tt $life:tt $ret:ident (unsafe trait $($tail:tt)*) -> $out:tt $stack:tt) => {
248 block!(@scan_item $paren $life $ret (unsafe trait $($tail)*) -> $out $stack)
249 };
250 (@scan $paren:tt $life:tt $ret:ident (impl $($tail:tt)*) -> $out:tt $stack:tt) => {
251 block!(@scan_item $paren $life $ret (impl $($tail)*) -> $out $stack)
252 };
253 (@scan $paren:tt $life:tt $ret:ident (unsafe impl $($tail:tt)*) -> $out:tt $stack:tt) => {
254 block!(@scan_item $paren $life $ret (unsafe impl $($tail)*) -> $out $stack)
255 };
256 (@scan $paren:tt $life:tt $ret:ident (fn $($tail:tt)*) -> $out:tt $stack:tt) => {
257 block!(@scan_item $paren $life $ret (fn $($tail)*) -> $out $stack)
258 };
259 (@scan $paren:tt $life:tt $ret:ident (unsafe fn $($tail:tt)*) -> $out:tt $stack:tt) => {
260 block!(@scan_item $paren $life $ret (unsafe fn $($tail)*) -> $out $stack)
261 };
262 (@scan $paren:tt $life:tt $ret:ident (type $($tail:tt)*) -> $out:tt $stack:tt) => {
263 block!(@scan_item $paren $life $ret (type $($tail)*) -> $out $stack)
264 };
265 (@scan $paren:tt $life:tt $ret:ident (enum $($tail:tt)*) -> $out:tt $stack:tt) => {
266 block!(@scan_item $paren $life $ret (enum $($tail)*) -> $out $stack)
267 };
268 (@scan $paren:tt $life:tt $ret:ident (struct $($tail:tt)*) -> $out:tt $stack:tt) => {
269 block!(@scan_item $paren $life $ret (struct $($tail)*) -> $out $stack)
270 };
271
272 (@scan $paren:tt $life:tt $ret:ident ({ $($inner:tt)* } $($tail:tt)*) -> $out:tt ($stack:tt $lp:tt $init:tt)) => {
274 block!(@scan {} $life $ret ($($inner)*) -> ()
275 (($paren ($($tail)*) -> $out $stack) $lp $init))
276 };
277 (@scan $paren:tt $life:tt $ret:ident (( $($inner:tt)* ) $($tail:tt)*) -> $out:tt ($stack:tt $lp:tt $init:tt)) => {
278 block!(@scan () $life $ret ($($inner)*) -> ()
279 (($paren ($($tail)*) -> $out $stack) $lp $init))
280 };
281 (@scan $paren:tt $life:tt $ret:ident ([ $($inner:tt)* ] $($tail:tt)*) -> $out:tt ($stack:tt $lp:tt $init:tt)) => {
282 block!(@scan [] $life $ret ($($inner)*) -> ()
283 (($paren ($($tail)*) -> $out $stack) $lp $init))
284 };
285
286 (@scan $paren:tt $life:tt $ret:ident ($head:tt $($tail:tt)*) -> ($($out:tt)*) $stack:tt) => {
288 block!(@scan $paren $life $ret ($($tail)*) -> ($($out)* $head) $stack)
289 };
290
291 (@up $life:tt $ret:ident $thing:tt (($paren:tt $tail:tt -> ($($out:tt)*) $stack:tt) $lp:tt $init:tt)) => {
294 block!(@scan $paren $life $ret $tail -> ($($out)* $thing) ($stack $lp $init))
295 };
296
297 ($life:tt: { $($body:tt)* }) => {
299 block!(@scan {} $life _ret ($($body)*) -> () (() () ()))
300 };
312
313 ($life:tt: loop { $($body:tt)* }) => {
315 block!(@scan {} $life _ret ($($body)*) -> () (() (loop) (= ())))
316 };
317}
318
319#[cfg(test)]
320mod tests {
321 #[test]
322 fn it_works() {
323 let flag = true;
324 let x = block!('a: {
325 if flag { break 'a "early exit"; }
326 "normal exit"
327 });
328 assert_eq!(x, "early exit");
329 }
330
331 #[test]
332 fn shadowing() {
333 let flag = false;
334 let x = block!('b: {
335 if flag { break 'b "early exit"; }
336 let _y = block!('c: {
337 if flag { break 'b "inner early exit"; };
338 String::from("inner normal exit")
339 });
340
341 #[block(ignore)]
342 {
343 #[allow(dead_code)]
344 fn f() -> i32 {
345 block!('b: {
346 break 'b 42;
347 })
348 }
349 }
350 #[allow(dead_code)]
351 fn g() {
352 block!('b: {
353 break 'b 42;
354 });
355
356 while false {
357 break;
358 }
359 while false {
360 continue;
361 }
362 'b: while false {
363 continue 'b;
364 }
365 }
366
367 enum Foo { Bar(i32) }
368 let closure = move |Foo::Bar(x): Foo| -> i32 {
369 x + block!('d: {
370 break 'd 42;
371 })
372 };
373 assert_eq!(closure(Foo::Bar(0)), 42);
374
375 "normal exit"
376 });
377 assert_eq!(x, "normal exit");
378
379 'e: for i in 1..5 {
380 assert!(i >= 1 && i < 5);
381 block!('d: {
382 continue 'e;
385 });
386 }
387 }
388
389 #[test]
390 fn loops() {
391 assert_eq!(42, block!('a: loop { break 'a 42 }));
392 assert_eq!((), block!('a: {})); let mut v = vec![];
395 let mut i = 0;
396 block!('a: loop {
397 i += 1;
398 if i == 5 {
399 continue 'a;
400 } else if i == 10 {
401 break 'a;
402 }
403 v.push(i);
404 });
405 assert_eq!(&*v, &[1, 2, 3, 4, 6, 7, 8, 9]);
406 }
407}
408