Skip to content

Commit 92979cd

Browse files
committed
Add codegen support for goto
1 parent 0b7b69a commit 92979cd

6 files changed

Lines changed: 120 additions & 4 deletions

File tree

cpp2rust/converter/converter.cpp

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

367+
void Converter::EmitHoistedDecls(clang::CompoundStmt *body) {
368+
for (auto *child : body->body()) {
369+
if (auto *decl_stmt = clang::dyn_cast<clang::DeclStmt>(child)) {
370+
for (auto *decl : decl_stmt->decls()) {
371+
if (auto *var = clang::dyn_cast<clang::VarDecl>(decl);
372+
var && var->isLocalVarDecl() && !IsGlobalVar(var)) {
373+
hoisted_decls_.insert(var);
374+
if (ConvertVarDeclSkipInit(var)) {
375+
StrCat(token::kAssign, ConvertVarDefaultInit(var->getType()),
376+
token::kSemiColon);
377+
}
378+
}
379+
}
380+
}
381+
}
382+
}
383+
384+
void Converter::ConvertGotoBlock(clang::CompoundStmt *body) {
385+
PushHoistedDecls push(hoisted_decls_);
386+
EmitHoistedDecls(body);
387+
388+
StrCat("goto_block!");
389+
{
390+
PushParen paren(*this);
391+
PushBrace outer(*this);
392+
StrCat("'__entry: ");
393+
std::optional<PushBrace> arm;
394+
arm.emplace(*this);
395+
for (auto *child : body->body()) {
396+
if (auto *label = clang::dyn_cast<clang::LabelStmt>(child)) {
397+
arm.reset();
398+
StrCat(std::format("'{}: ", label->getDecl()->getName().str()));
399+
arm.emplace(*this);
400+
Convert(label->getSubStmt());
401+
} else {
402+
Convert(child);
403+
}
404+
}
405+
}
406+
StrCat(token::kSemiColon);
407+
}
408+
367409
void Converter::ConvertFunctionBody(clang::FunctionDecl *decl) {
410+
if (auto compound = clang::dyn_cast<clang::CompoundStmt>(decl->getBody())) {
411+
if (CompoundHasTopLevelLabel(compound)) {
412+
ConvertGotoBlock(compound);
413+
if (!decl->getReturnType()->isVoidType()) {
414+
StrCat(R"(panic!("ub: non-void function does not return a value"))");
415+
}
416+
return;
417+
}
418+
}
419+
368420
Convert(decl->getBody());
369421
if (!decl->getReturnType()->isVoidType()) {
370422
if (auto compound = clang::dyn_cast<clang::CompoundStmt>(decl->getBody())) {
@@ -422,9 +474,11 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
422474
auto *method_or_null =
423475
curr_function_ ? clang::dyn_cast<clang::CXXMethodDecl>(curr_function_)
424476
: nullptr;
425-
if (!qual_type.isConstQualified() && !qual_type->isReferenceType() &&
426-
((method_or_null == nullptr) || !method_or_null->isVirtual()) &&
427-
!IsGlobalVar(decl)) {
477+
if (hoisted_decls_.contains(decl) && !qual_type->isReferenceType()) {
478+
StrCat("mut");
479+
} else if (!qual_type.isConstQualified() && !qual_type->isReferenceType() &&
480+
((method_or_null == nullptr) || !method_or_null->isVirtual()) &&
481+
!IsGlobalVar(decl)) {
428482
StrCat(keyword_mut_);
429483
}
430484

@@ -468,6 +522,14 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
468522
}
469523

470524
void Converter::ConvertVarDecl(clang::VarDecl *decl) {
525+
if (hoisted_decls_.contains(decl)) {
526+
if (decl->hasInit()) {
527+
StrCat(GetNamedDeclAsString(decl), token::kAssign);
528+
ConvertVarInit(decl->getType(), decl->getInit());
529+
StrCat(token::kSemiColon);
530+
}
531+
return;
532+
}
471533
if (!ConvertVarDeclSkipInit(decl)) {
472534
// Skip global variables declared extern
473535
return;
@@ -985,6 +1047,10 @@ bool Converter::Convert(clang::Stmt *stmt) {
9851047
}
9861048

9871049
bool Converter::VisitCompoundStmt(clang::CompoundStmt *stmt) {
1050+
if (CompoundHasTopLevelLabel(stmt)) {
1051+
ConvertGotoBlock(stmt);
1052+
return false;
1053+
}
9881054
for (auto *child : stmt->body()) {
9891055
Convert(child);
9901056
}
@@ -1011,6 +1077,11 @@ bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) {
10111077
return false;
10121078
}
10131079

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

cpp2rust/converter/converter.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
8282

8383
virtual void ConvertFunctionBody(clang::FunctionDecl *decl);
8484

85+
void ConvertGotoBlock(clang::CompoundStmt *body);
86+
87+
void EmitHoistedDecls(clang::CompoundStmt *body);
88+
8589
virtual bool VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl);
8690

8791
virtual bool VisitVarDecl(clang::VarDecl *decl);
@@ -125,6 +129,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
125129

126130
virtual bool VisitReturnStmt(clang::ReturnStmt *stmt);
127131

132+
virtual bool VisitGotoStmt(clang::GotoStmt *stmt);
133+
128134
void ConvertCondition(clang::Expr *cond);
129135

130136
virtual bool VisitIfStmt(clang::IfStmt *stmt);
@@ -646,6 +652,24 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
646652

647653
std::unordered_set<const clang::VarDecl *> map_iter_decls_;
648654

655+
// Local variables hoisted outside a goto_block so that all labels can see and
656+
// use the variables.
657+
std::unordered_set<const clang::VarDecl *> hoisted_decls_;
658+
class PushHoistedDecls {
659+
public:
660+
PushHoistedDecls(std::unordered_set<const clang::VarDecl *> &field)
661+
: field_(field), saved_(std::move(field)) {
662+
field_.clear();
663+
}
664+
~PushHoistedDecls() { field_ = std::move(saved_); }
665+
PushHoistedDecls(const PushHoistedDecls &) = delete;
666+
PushHoistedDecls &operator=(const PushHoistedDecls &) = delete;
667+
668+
private:
669+
std::unordered_set<const clang::VarDecl *> &field_;
670+
std::unordered_set<const clang::VarDecl *> saved_;
671+
};
672+
649673
struct ScopedMapIterDecl {
650674
Converter &c;
651675
const clang::VarDecl *decl;

cpp2rust/converter/converter_lib.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,8 @@ static bool SwitchCaseHasFallthrough(clang::Stmt *stmt) {
811811
}
812812
if (clang::isa<clang::BreakStmt>(stmt) ||
813813
clang::isa<clang::ContinueStmt>(stmt) ||
814-
clang::isa<clang::ReturnStmt>(stmt)) {
814+
clang::isa<clang::ReturnStmt>(stmt) ||
815+
clang::isa<clang::GotoStmt>(stmt)) {
815816
return false;
816817
}
817818
return true;
@@ -829,6 +830,15 @@ bool SwitchHasFallthrough(clang::SwitchStmt *stmt) {
829830
return false;
830831
}
831832

833+
bool CompoundHasTopLevelLabel(const clang::CompoundStmt *compound) {
834+
for (const auto *child : compound->body()) {
835+
if (clang::isa<clang::LabelStmt>(child)) {
836+
return true;
837+
}
838+
}
839+
return false;
840+
}
841+
832842
std::string_view Trim(std::string_view s) {
833843
auto is_space = [](unsigned char c) { return std::isspace(c); };
834844
auto b = std::find_if_not(s.begin(), s.end(), is_space);

cpp2rust/converter/converter_lib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ std::vector<clang::Stmt *> GetSwitchCaseBody(clang::CompoundStmt *body,
169169

170170
bool SwitchHasFallthrough(clang::SwitchStmt *stmt);
171171

172+
bool CompoundHasTopLevelLabel(const clang::CompoundStmt *compound);
173+
172174
std::string_view Trim(std::string_view s);
173175

174176
void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix);

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,13 @@ bool ConverterRefCount::ConvertLambdaVarDecl(clang::VarDecl *decl) {
621621
return false;
622622
}
623623

624+
bool ConverterRefCount::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
625+
bool unboxed = in_function_formals_;
626+
PushConversionKind push(*this, unboxed ? ConversionKind::Unboxed
627+
: ConversionKind::FullRefCount);
628+
return Converter::ConvertVarDeclSkipInit(decl);
629+
}
630+
624631
void ConverterRefCount::ConvertGlobalVarDecl(clang::VarDecl *decl) {
625632
StrCat("thread_local!");
626633
{

cpp2rust/converter/models/converter_refcount.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class ConverterRefCount final : public Converter {
5757

5858
void ConvertVaListVarDecl(clang::VarDecl *decl) override;
5959

60+
bool ConvertVarDeclSkipInit(clang::VarDecl *decl) override;
61+
6062
bool ConvertLambdaVarDecl(clang::VarDecl *decl) override;
6163

6264
bool VisitDeclRefExpr(clang::DeclRefExpr *expr) override;

0 commit comments

Comments
 (0)