Skip to content

Commit e870e35

Browse files
Preserve exit targets in nested control-flow macros
Signed-off-by: Perfloop Agent <agent@perfloop.ai> Signed-off-by: Tomás Senart <ts@perfloop.ai>
1 parent 5a7ee97 commit e870e35

4 files changed

Lines changed: 247 additions & 4 deletions

File tree

‎libcc2rs-macros/src/state_machine.rs‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,7 @@ impl GotoRewriter<'_> {
189189

190190
fn recurse_into_inner_goto_block(&mut self, mac: &mut syn::Macro) -> bool {
191191
if mac.path.is_ident("switch") || mac.path.is_ident("goto_block") {
192-
if let Ok(mut inner) = syn::parse2::<Expr>(mac.tokens.clone()) {
193-
self.visit_expr_mut(&mut inner);
194-
mac.tokens = quote!(#inner);
195-
}
192+
visit_control_flow_macro(self, mac, true);
196193
return true;
197194
}
198195
false
@@ -284,12 +281,47 @@ impl StateMachine for SwitchStateMachine {
284281
}
285282
}
286283

284+
// Macro tokens are opaque to syn's normal traversal. A nested goto block is
285+
// transparent to exits; a nested switch owns breaks in its arms, but not in
286+
// its condition or guards. Neither macro owns continues.
287+
fn visit_control_flow_macro(
288+
visitor: &mut impl VisitMut,
289+
mac: &mut syn::Macro,
290+
visit_switch_arms: bool,
291+
) {
292+
let Some(name) = mac.path.segments.last().map(|s| &s.ident) else {
293+
return;
294+
};
295+
if name == "goto_block"
296+
&& let Ok(mut body) = syn::parse2::<Expr>(mac.tokens.clone())
297+
{
298+
visitor.visit_expr_mut(&mut body);
299+
mac.tokens = quote!(#body);
300+
} else if name == "switch"
301+
&& let Ok(mut body) = syn::parse2::<syn::ExprMatch>(mac.tokens.clone())
302+
{
303+
visitor.visit_expr_mut(&mut body.expr);
304+
for arm in &mut body.arms {
305+
if let Some((_, guard)) = &mut arm.guard {
306+
visitor.visit_expr_mut(guard);
307+
}
308+
if visit_switch_arms {
309+
visitor.visit_expr_mut(&mut arm.body);
310+
}
311+
}
312+
mac.tokens = quote!(#body);
313+
}
314+
}
315+
287316
// Rewrite break into break '__sm
288317
struct ExitSwitchRewriter {
289318
label: Lifetime,
290319
}
291320

292321
impl VisitMut for ExitSwitchRewriter {
322+
fn visit_macro_mut(&mut self, mac: &mut syn::Macro) {
323+
visit_control_flow_macro(self, mac, false);
324+
}
293325
fn visit_expr_break_mut(&mut self, node: &mut ExprBreak) {
294326
if node.label.is_none() {
295327
node.label = Some(self.label.clone());
@@ -333,6 +365,9 @@ impl PropagateRewriter {
333365
}
334366

335367
impl VisitMut for PropagateRewriter {
368+
fn visit_macro_mut(&mut self, mac: &mut syn::Macro) {
369+
visit_control_flow_macro(self, mac, matches!(self.kind, ControlKind::Continue));
370+
}
336371
fn visit_expr_mut(&mut self, expr: &mut Expr) {
337372
let hit = match self.kind {
338373
ControlKind::Break => {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <assert.h>
2+
3+
int main(void) {
4+
int visits = 0;
5+
int tail = 0;
6+
for (int i = 0; i < 3; i++) {
7+
outer:
8+
visits++;
9+
{
10+
inner:
11+
if (i == 0) {
12+
break;
13+
}
14+
}
15+
tail++;
16+
}
17+
assert(visits == 1);
18+
assert(tail == 0);
19+
20+
visits = 0;
21+
tail = 0;
22+
for (int i = 0; i < 3; i++) {
23+
switch (i) {
24+
case 0: {
25+
switch_outer:
26+
visits++;
27+
{
28+
switch_inner:
29+
if (i == 0) {
30+
break;
31+
}
32+
}
33+
tail++;
34+
}
35+
case 1:
36+
tail += 10;
37+
break;
38+
default:
39+
tail += 100;
40+
break;
41+
}
42+
}
43+
assert(visits == 1);
44+
assert(tail == 110);
45+
return 0;
46+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
pub fn main() {
10+
__cpp2rust_init_globals();
11+
std::process::exit(main_0());
12+
}
13+
fn main_0() -> i32 {
14+
let mut visits: i32 = 0;
15+
let mut tail: i32 = 0;
16+
let mut i: i32 = 0;
17+
'loop_: while (((i < 3) as i32) != 0) {
18+
goto_block!({
19+
'__entry: {}
20+
'outer: {
21+
visits.postfix_inc();
22+
{
23+
goto_block!({
24+
'__entry: {}
25+
'inner: {
26+
if (((i == 0) as i32) != 0) {
27+
break;
28+
}
29+
}
30+
});
31+
}
32+
tail.postfix_inc();
33+
}
34+
});
35+
i.postfix_inc();
36+
}
37+
assert!((((visits == 1) as i32) != 0));
38+
assert!((((tail == 0) as i32) != 0));
39+
visits = 0;
40+
tail = 0;
41+
let mut i: i32 = 0;
42+
'loop_: while (((i < 3) as i32) != 0) {
43+
switch!(match i {
44+
__v if __v == 0 => {
45+
{
46+
goto_block!({
47+
'__entry: {}
48+
'switch_outer: {
49+
visits.postfix_inc();
50+
{
51+
goto_block!({
52+
'__entry: {}
53+
'switch_inner: {
54+
if (((i == 0) as i32) != 0) {
55+
break;
56+
}
57+
}
58+
});
59+
}
60+
tail.postfix_inc();
61+
}
62+
});
63+
}
64+
}
65+
__v if __v == 1 => {
66+
tail += 10;
67+
break;
68+
}
69+
_ => {
70+
tail += 100;
71+
break;
72+
}
73+
});
74+
i.postfix_inc();
75+
}
76+
assert!((((visits == 1) as i32) != 0));
77+
assert!((((tail == 110) as i32) != 0));
78+
return 0;
79+
}
80+
pub fn __cpp2rust_init_globals() {}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
pub fn main() {
10+
unsafe {
11+
__cpp2rust_init_globals();
12+
std::process::exit(main_0() as i32);
13+
}
14+
}
15+
unsafe fn main_0() -> i32 {
16+
let mut visits: i32 = 0;
17+
let mut tail: i32 = 0;
18+
let mut i: i32 = 0;
19+
'loop_: while ((((i) < (3)) as i32) != 0) {
20+
goto_block!({
21+
'__entry: {}
22+
'outer: {
23+
visits.postfix_inc();
24+
{
25+
goto_block!({
26+
'__entry: {}
27+
'inner: {
28+
if ((((i) == (0)) as i32) != 0) {
29+
break;
30+
}
31+
}
32+
});
33+
}
34+
tail.postfix_inc();
35+
}
36+
});
37+
i.postfix_inc();
38+
}
39+
assert!(((((visits) == (1)) as i32) != 0));
40+
assert!(((((tail) == (0)) as i32) != 0));
41+
visits = 0;
42+
tail = 0;
43+
let mut i: i32 = 0;
44+
'loop_: while ((((i) < (3)) as i32) != 0) {
45+
switch!(match i {
46+
__v if __v == 0 => {
47+
{
48+
goto_block!({
49+
'__entry: {}
50+
'switch_outer: {
51+
visits.postfix_inc();
52+
{
53+
goto_block!({
54+
'__entry: {}
55+
'switch_inner: {
56+
if ((((i) == (0)) as i32) != 0) {
57+
break;
58+
}
59+
}
60+
});
61+
}
62+
tail.postfix_inc();
63+
}
64+
});
65+
}
66+
}
67+
__v if __v == 1 => {
68+
tail += 10;
69+
break;
70+
}
71+
_ => {
72+
tail += 100;
73+
break;
74+
}
75+
});
76+
i.postfix_inc();
77+
}
78+
assert!(((((visits) == (1)) as i32) != 0));
79+
assert!(((((tail) == (110)) as i32) != 0));
80+
return 0;
81+
}
82+
pub unsafe fn __cpp2rust_init_globals() {}

0 commit comments

Comments
 (0)