Skip to content

Commit a785482

Browse files
committed
Add codegen support for goto (Cpp2Rust#163)
Depends on Cpp2Rust#161. See Cpp2Rust#161 for a more detailed description of how goto is implemented. On the codegen part, this PR emits the goto_block and goto macros when it hits functions that use labels and gotos internally. It also hoists local variables at the start of the function so that all goto_block arms can see and use the local variables.
1 parent a605853 commit a785482

5 files changed

Lines changed: 80 additions & 6 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,59 @@ bool Converter::VisitFunctionDecl(clang::FunctionDecl *decl) {
363363
return false;
364364
}
365365

366+
void Converter::EmitHoistedDecls(clang::CompoundStmt *body) {
367+
for (auto *child : body->body()) {
368+
if (auto *decl_stmt = clang::dyn_cast<clang::DeclStmt>(child)) {
369+
for (auto *decl : decl_stmt->decls()) {
370+
if (auto *var = clang::dyn_cast<clang::VarDecl>(decl);
371+
var && var->isLocalVarDecl() && !IsGlobalVar(var)) {
372+
hoisted_decls_.insert(var);
373+
if (ConvertVarDeclSkipInit(var)) {
374+
StrCat(token::kAssign, ConvertVarDefaultInit(var->getType()),
375+
token::kSemiColon);
376+
}
377+
}
378+
}
379+
}
380+
}
381+
}
382+
383+
void Converter::ConvertGotoBlock(clang::CompoundStmt *body) {
384+
PushHoistedDecls push(hoisted_decls_);
385+
EmitHoistedDecls(body);
386+
387+
StrCat("goto_block!");
388+
{
389+
PushParen paren(*this);
390+
PushBrace outer(*this);
391+
StrCat("'__entry: ");
392+
std::optional<PushBrace> arm;
393+
arm.emplace(*this);
394+
for (auto *child : body->body()) {
395+
if (auto *label = clang::dyn_cast<clang::LabelStmt>(child)) {
396+
arm.reset();
397+
StrCat(std::format("'{}: ", label->getDecl()->getName().str()));
398+
arm.emplace(*this);
399+
Convert(label->getSubStmt());
400+
} else {
401+
Convert(child);
402+
}
403+
}
404+
}
405+
StrCat(token::kSemiColon);
406+
}
407+
366408
void Converter::ConvertFunctionBody(clang::FunctionDecl *decl) {
409+
if (auto compound = clang::dyn_cast<clang::CompoundStmt>(decl->getBody())) {
410+
if (CompoundHasTopLevelLabel(compound)) {
411+
ConvertGotoBlock(compound);
412+
if (!decl->getReturnType()->isVoidType()) {
413+
StrCat(R"(panic!("ub: non-void function does not return a value"))");
414+
}
415+
return;
416+
}
417+
}
418+
367419
Convert(decl->getBody());
368420
if (!decl->getReturnType()->isVoidType()) {
369421
if (auto compound = clang::dyn_cast<clang::CompoundStmt>(decl->getBody())) {
@@ -466,7 +518,20 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
466518
}
467519
}
468520

521+
void Converter::EmitHoistedInArmAssignment(clang::VarDecl *decl) {
522+
if (!decl->hasInit()) {
523+
return;
524+
}
525+
StrCat(GetNamedDeclAsString(decl), token::kAssign);
526+
ConvertVarInit(decl->getType(), decl->getInit());
527+
StrCat(token::kSemiColon);
528+
}
529+
469530
void Converter::ConvertVarDecl(clang::VarDecl *decl) {
531+
if (hoisted_decls_.contains(decl)) {
532+
EmitHoistedInArmAssignment(decl);
533+
return;
534+
}
470535
if (!ConvertVarDeclSkipInit(decl)) {
471536
// Skip global variables declared extern
472537
return;
@@ -984,6 +1049,10 @@ bool Converter::Convert(clang::Stmt *stmt) {
9841049
}
9851050

9861051
bool Converter::VisitCompoundStmt(clang::CompoundStmt *stmt) {
1052+
if (CompoundHasTopLevelLabel(stmt)) {
1053+
ConvertGotoBlock(stmt);
1054+
return false;
1055+
}
9871056
for (auto *child : stmt->body()) {
9881057
Convert(child);
9891058
}
@@ -1010,6 +1079,11 @@ bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) {
10101079
return false;
10111080
}
10121081

1082+
bool Converter::VisitGotoStmt(clang::GotoStmt *stmt) {
1083+
StrCat(std::format("goto!('{})", stmt->getLabel()->getName().str()));
1084+
return false;
1085+
}
1086+
10131087
void Converter::ConvertCondition(clang::Expr *cond) {
10141088
PushExprKind push(*this, ExprKind::RValue);
10151089
Convert(NormalizeToBool(cond, ctx_));

tests/unit/out/refcount/goto_cleanup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub fn from_switch_2(n: i32) -> i32 {
5656
'switch: {
5757
let __match_cond = (*n.borrow());
5858
match __match_cond {
59-
__v if __v == 1 => {
59+
v if v == 1 => {
6060
(*ret.borrow_mut()) = 10;
6161
goto!('out);
6262
}

tests/unit/out/refcount/goto_switch_fallthrough.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ pub fn sm_0(n: i32) -> i32 {
1313
'__entry: {
1414
*ret.borrow_mut() = 0;
1515
switch!(match (*n.borrow()) {
16-
__v if __v == 0 => {
16+
v if v == 0 => {
1717
(*ret.borrow_mut()) += 1;
1818
}
19-
__v if __v == 1 => {
19+
v if v == 1 => {
2020
(*ret.borrow_mut()) += 10;
2121
goto!('out);
2222
}

tests/unit/out/unsafe/goto_cleanup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub unsafe fn from_switch_2(mut n: i32) -> i32 {
5353
'switch: {
5454
let __match_cond = n;
5555
match __match_cond {
56-
__v if __v == 1 => {
56+
v if v == 1 => {
5757
ret = 10;
5858
goto!('out);
5959
}

tests/unit/out/unsafe/goto_switch_fallthrough.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ pub unsafe fn sm_0(mut n: i32) -> i32 {
1212
'__entry: {
1313
ret = 0;
1414
switch!(match n {
15-
__v if __v == 0 => {
15+
v if v == 0 => {
1616
ret += 1;
1717
}
18-
__v if __v == 1 => {
18+
v if v == 1 => {
1919
ret += 10;
2020
goto!('out);
2121
}

0 commit comments

Comments
 (0)