Skip to content

Commit 860c804

Browse files
committed
Translate goto label attached to switch case
1 parent 0097f64 commit 860c804

8 files changed

Lines changed: 184 additions & 84 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <llvm/ADT/DenseMap.h>
1111
#include <llvm/Support/ConvertUTF.h>
1212

13+
#include <algorithm>
1314
#include <format>
1415
#include <utility>
1516

@@ -3180,46 +3181,53 @@ bool Converter::ConvertSwitchCaseCondition(clang::SwitchCase *stmt) {
31803181
}
31813182

31823183
if (clang::isa<clang::CaseStmt>(last)) {
3183-
StrCat(" => {");
3184+
StrCat(" => ");
31843185
} else /* DefaultStmt */ {
3185-
StrCat("_ => {");
3186+
StrCat("_ => ");
31863187
}
31873188
return false;
31883189
}
31893190

3190-
void Converter::EmitSwitchArm(clang::CompoundStmt *body, clang::SwitchCase *sc,
3191-
bool is_default) {
3191+
void Converter::EmitSwitchArm(const SwitchArm &arm, bool is_default) {
31923192
if (is_default) {
3193-
StrCat("_ => {");
3193+
StrCat("_ => ");
31943194
} else {
31953195
StrCat("__v if __v == ");
3196-
ConvertSwitchCaseCondition(sc);
3196+
ConvertSwitchCaseCondition(arm.head);
31973197
}
3198-
for (auto *t : GetSwitchCaseBody(body, sc)) {
3198+
if (!arm.label.empty()) {
3199+
StrCat(std::format("'{}: ", arm.label.str()));
3200+
}
3201+
StrCat(token::kOpenCurlyBracket);
3202+
for (auto *t : arm.body) {
31993203
Convert(t);
32003204
}
32013205
StrCat("},");
32023206
}
32033207

32043208
bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) {
3205-
bool has_fallthrough = SwitchHasFallthrough(stmt);
3206-
PushBreakTarget push(break_target_, has_fallthrough
3207-
? BreakTarget::FallthroughSwitch
3208-
: BreakTarget::Switch);
32093209
auto *body = clang::dyn_cast<clang::CompoundStmt>(stmt->getBody());
32103210
assert(body);
3211+
auto arms = AnalyzeSwitchArms(body);
3212+
3213+
bool needs_switch_macro = std::ranges::any_of(arms, [](const SwitchArm &arm) {
3214+
return !arm.label.empty() || arm.has_fallthrough;
3215+
});
3216+
3217+
PushBreakTarget push(break_target_, needs_switch_macro
3218+
? BreakTarget::FallthroughSwitch
3219+
: BreakTarget::Switch);
32113220

3212-
if (has_fallthrough) {
3213-
// Use the switch-with-fallthrough macro
3221+
if (needs_switch_macro) {
32143222
StrCat("switch!");
32153223
} else {
32163224
StrCat("'switch:");
32173225
}
32183226

3219-
PushParen switch_macro_paren(*this, has_fallthrough);
3220-
PushBrace switch_label_brace(*this, !has_fallthrough);
3227+
PushParen switch_macro_paren(*this, needs_switch_macro);
3228+
PushBrace switch_label_brace(*this, !needs_switch_macro);
32213229

3222-
if (has_fallthrough) {
3230+
if (needs_switch_macro) {
32233231
StrCat("match", ToString(stmt->getCond()));
32243232
} else {
32253233
StrCat(
@@ -3229,17 +3237,17 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) {
32293237

32303238
PushBrace match_brace(*this);
32313239

3232-
clang::SwitchCase *default_case = nullptr;
3233-
for (auto *sc : GetTopLevelSwitchCases(stmt)) {
3234-
if (SwitchCaseContainsDefault(sc)) {
3235-
default_case = sc;
3240+
const SwitchArm *default_arm = nullptr;
3241+
for (const auto &arm : arms) {
3242+
if (arm.is_default_case) {
3243+
default_arm = &arm;
32363244
continue;
32373245
}
3238-
EmitSwitchArm(body, sc, /*is_default=*/false);
3246+
EmitSwitchArm(arm, /*is_default=*/false);
32393247
}
32403248

3241-
if (default_case) {
3242-
EmitSwitchArm(body, default_case, /*is_default=*/true);
3249+
if (default_arm) {
3250+
EmitSwitchArm(*default_arm, /*is_default=*/true);
32433251
} else {
32443252
StrCat(R"( _ => {})");
32453253
}

cpp2rust/converter/converter.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <utility>
1616
#include <vector>
1717

18+
#include "converter/converter_lib.h"
1819
#include "converter/lex.h"
1920
#include "converter/translation_rule.h"
2021
#include "logging.h"
@@ -365,8 +366,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
365366

366367
virtual bool VisitSwitchStmt(clang::SwitchStmt *stmt);
367368

368-
void EmitSwitchArm(clang::CompoundStmt *body, clang::SwitchCase *sc,
369-
bool is_default);
369+
void EmitSwitchArm(const SwitchArm &arm, bool is_default);
370370

371371
bool ConvertSwitchCaseCondition(clang::SwitchCase *stmt);
372372

cpp2rust/converter/converter_lib.cpp

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -808,20 +808,15 @@ clang::Expr *NormalizeToBool(clang::Expr *expr, clang::ASTContext &ctx) {
808808
/*BasePath=*/nullptr, clang::VK_PRValue, clang::FPOptionsOverride());
809809
}
810810

811-
std::vector<clang::SwitchCase *>
812-
GetTopLevelSwitchCases(clang::SwitchStmt *stmt) {
813-
std::vector<clang::SwitchCase *> cases;
814-
if (auto *body = llvm::dyn_cast<clang::CompoundStmt>(stmt->getBody())) {
815-
for (auto *s : body->body()) {
816-
if (auto *sc = clang::dyn_cast<clang::SwitchCase>(s)) {
817-
cases.push_back(sc);
818-
}
819-
}
811+
static clang::Stmt *GetLastStmtOfSwitchCase(clang::SwitchCase *c) {
812+
clang::Stmt *cur = c->getSubStmt();
813+
while (auto *sc = clang::dyn_cast<clang::SwitchCase>(cur)) {
814+
cur = sc->getSubStmt();
820815
}
821-
return cases;
816+
return cur;
822817
}
823818

824-
bool SwitchCaseContainsDefault(clang::SwitchCase *c) {
819+
static bool CaseChainHasDefault(clang::SwitchCase *c) {
825820
for (clang::Stmt *cur = c;;) {
826821
if (clang::isa<clang::DefaultStmt>(cur)) {
827822
return true;
@@ -832,32 +827,6 @@ bool SwitchCaseContainsDefault(clang::SwitchCase *c) {
832827
}
833828
cur = sc->getSubStmt();
834829
}
835-
return false;
836-
}
837-
838-
static clang::Stmt *GetLastStmtOfSwitchCase(clang::SwitchCase *c) {
839-
clang::Stmt *cur = c->getSubStmt();
840-
while (auto *sc = clang::dyn_cast<clang::SwitchCase>(cur)) {
841-
cur = sc->getSubStmt();
842-
}
843-
return cur;
844-
}
845-
846-
std::vector<clang::Stmt *> GetSwitchCaseBody(clang::CompoundStmt *body,
847-
clang::SwitchCase *head) {
848-
std::vector<clang::Stmt *> out;
849-
out.push_back(GetLastStmtOfSwitchCase(head));
850-
auto it = body->body_begin(), end = body->body_end();
851-
while (it != end && *it != head) {
852-
++it;
853-
}
854-
assert(it != end);
855-
++it;
856-
while (it != end && !clang::isa<clang::SwitchCase>(*it)) {
857-
out.push_back(*it);
858-
++it;
859-
}
860-
return out;
861830
}
862831

863832
static bool SwitchCaseHasFallthrough(clang::Stmt *stmt) {
@@ -879,16 +848,34 @@ static bool SwitchCaseHasFallthrough(clang::Stmt *stmt) {
879848
return true;
880849
}
881850

882-
bool SwitchHasFallthrough(clang::SwitchStmt *stmt) {
883-
if (auto *body = clang::dyn_cast<clang::CompoundStmt>(stmt->getBody())) {
884-
for (auto top_level_case : GetTopLevelSwitchCases(stmt)) {
885-
auto arm = GetSwitchCaseBody(body, top_level_case);
886-
if (arm.empty() || SwitchCaseHasFallthrough(arm.back())) {
887-
return true;
888-
}
851+
std::vector<SwitchArm> AnalyzeSwitchArms(clang::CompoundStmt *body) {
852+
std::vector<SwitchArm> arms;
853+
for (clang::Stmt *s : body->body()) {
854+
llvm::StringRef label;
855+
clang::Stmt *inner = s;
856+
if (auto *outer = clang::dyn_cast<clang::LabelStmt>(inner)) {
857+
label = outer->getDecl()->getName();
858+
do {
859+
inner = clang::cast<clang::LabelStmt>(inner)->getSubStmt();
860+
} while (clang::isa<clang::LabelStmt>(inner));
861+
}
862+
863+
if (auto *sc = clang::dyn_cast<clang::SwitchCase>(inner)) {
864+
arms.push_back({sc,
865+
label,
866+
{GetLastStmtOfSwitchCase(sc)},
867+
CaseChainHasDefault(sc),
868+
/*has_fallthrough=*/false});
869+
} else if (!arms.empty()) {
870+
arms.back().body.push_back(s);
889871
}
890872
}
891-
return false;
873+
874+
for (SwitchArm &arm : arms) {
875+
arm.has_fallthrough =
876+
arm.body.empty() || SwitchCaseHasFallthrough(arm.body.back());
877+
}
878+
return arms;
892879
}
893880

894881
bool CompoundHasTopLevelLabel(const clang::CompoundStmt *compound) {

cpp2rust/converter/converter_lib.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,15 @@ bool ContainsVAArgExpr(const clang::Stmt *stmt);
169169

170170
clang::Expr *NormalizeToBool(clang::Expr *expr, clang::ASTContext &ctx);
171171

172-
std::vector<clang::SwitchCase *>
173-
GetTopLevelSwitchCases(clang::SwitchStmt *stmt);
174-
175-
bool SwitchCaseContainsDefault(clang::SwitchCase *c);
176-
177-
std::vector<clang::Stmt *> GetSwitchCaseBody(clang::CompoundStmt *body,
178-
clang::SwitchCase *head);
172+
struct SwitchArm {
173+
clang::SwitchCase *head;
174+
llvm::StringRef label;
175+
std::vector<clang::Stmt *> body;
176+
bool is_default_case;
177+
bool has_fallthrough;
178+
};
179179

180-
bool SwitchHasFallthrough(clang::SwitchStmt *stmt);
180+
std::vector<SwitchArm> AnalyzeSwitchArms(clang::CompoundStmt *body);
181181

182182
bool CompoundHasTopLevelLabel(const clang::CompoundStmt *compound);
183183

libcc2rs-macros/src/switch.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use proc_macro::TokenStream;
55
use syn::parse::{Parse, ParseStream};
6-
use syn::{Expr, Pat, parse_macro_input};
6+
use syn::{Expr, ExprBlock, Pat, parse_macro_input};
77

88
use crate::state_machine::{
99
Arm, DispatchCase, GotoStateMachine, StateMachine, StateMachineNames, SwitchStateMachine,
@@ -14,16 +14,23 @@ pub fn expand(input: TokenStream) -> TokenStream {
1414
let mut cases = Vec::with_capacity(arms.len());
1515
let mut cfg_arms = Vec::with_capacity(arms.len());
1616
for (i, a) in arms.into_iter().enumerate() {
17-
let label = format!("__c{}", i);
17+
let (label, body) = match a.body {
18+
Expr::Block(eb) if eb.label.is_some() => (
19+
eb.label.unwrap().name.ident.to_string(),
20+
Expr::Block(ExprBlock {
21+
attrs: eb.attrs,
22+
label: None,
23+
block: eb.block,
24+
}),
25+
),
26+
other => (format!("__c{}", i), other),
27+
};
1828
cases.push(DispatchCase {
1929
pat: a.pat,
2030
guard: a.guard,
2131
target: label.clone(),
2232
});
23-
cfg_arms.push(Arm {
24-
label,
25-
body: a.body,
26-
});
33+
cfg_arms.push(Arm { label, body });
2734
}
2835
SwitchStateMachine {
2936
goto: GotoStateMachine {

tests/unit/goto_switch_self_case.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <assert.h>
2+
3+
static int sm(int n) {
4+
int steps = 0;
5+
switch (n) {
6+
target:
7+
case 0:
8+
steps += 1;
9+
break;
10+
case 1:
11+
steps += 10;
12+
goto target;
13+
default:
14+
steps = -1;
15+
break;
16+
}
17+
return steps;
18+
}
19+
20+
int main(void) {
21+
assert(sm(0) == 1);
22+
assert(sm(1) == 11);
23+
assert(sm(7) == -1);
24+
return 0;
25+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 sm_0(n: i32) -> i32 {
10+
let n: Value<i32> = Rc::new(RefCell::new(n));
11+
let steps: Value<i32> = Rc::new(RefCell::new(0));
12+
switch!(match (*n.borrow()) {
13+
__v if __v == 0 => 'target: {
14+
(*steps.borrow_mut()) += 1;
15+
break;
16+
}
17+
__v if __v == 1 => {
18+
(*steps.borrow_mut()) += 10;
19+
goto!('target);
20+
}
21+
_ => {
22+
(*steps.borrow_mut()) = -1_i32;
23+
break;
24+
}
25+
});
26+
return (*steps.borrow());
27+
}
28+
pub fn main() {
29+
std::process::exit(main_0());
30+
}
31+
fn main_0() -> i32 {
32+
assert!((((({ sm_0(0,) }) == 1) as i32) != 0));
33+
assert!((((({ sm_0(1,) }) == 11) as i32) != 0));
34+
assert!((((({ sm_0(7,) }) == -1_i32) as i32) != 0));
35+
return 0;
36+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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 unsafe fn sm_0(mut n: i32) -> i32 {
10+
let mut steps: i32 = 0;
11+
switch!(match n {
12+
__v if __v == 0 => 'target: {
13+
steps += 1;
14+
break;
15+
}
16+
__v if __v == 1 => {
17+
steps += 10;
18+
goto!('target);
19+
}
20+
_ => {
21+
steps = -1_i32;
22+
break;
23+
}
24+
});
25+
return steps;
26+
}
27+
pub fn main() {
28+
unsafe {
29+
std::process::exit(main_0() as i32);
30+
}
31+
}
32+
unsafe fn main_0() -> i32 {
33+
assert!(((((unsafe { sm_0(0,) }) == (1)) as i32) != 0));
34+
assert!(((((unsafe { sm_0(1,) }) == (11)) as i32) != 0));
35+
assert!(((((unsafe { sm_0(7,) }) == (-1_i32)) as i32) != 0));
36+
return 0;
37+
}

0 commit comments

Comments
 (0)