Skip to content

Commit d7a77b8

Browse files
committed
Add codegen support for goto
1 parent a8105ab commit d7a77b8

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
@@ -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())) {
@@ -421,9 +473,11 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
421473
auto *method_or_null =
422474
curr_function_ ? clang::dyn_cast<clang::CXXMethodDecl>(curr_function_)
423475
: nullptr;
424-
if (!qual_type.isConstQualified() && !qual_type->isReferenceType() &&
425-
((method_or_null == nullptr) || !method_or_null->isVirtual()) &&
426-
!IsGlobalVar(decl)) {
476+
if (hoisted_decls_.contains(decl) && !qual_type->isReferenceType()) {
477+
StrCat("mut");
478+
} else if (!qual_type.isConstQualified() && !qual_type->isReferenceType() &&
479+
((method_or_null == nullptr) || !method_or_null->isVirtual()) &&
480+
!IsGlobalVar(decl)) {
427481
StrCat(keyword_mut_);
428482
}
429483

@@ -467,6 +521,14 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
467521
}
468522

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

9861048
bool Converter::VisitCompoundStmt(clang::CompoundStmt *stmt) {
1049+
if (CompoundHasTopLevelLabel(stmt)) {
1050+
ConvertGotoBlock(stmt);
1051+
return false;
1052+
}
9871053
for (auto *child : stmt->body()) {
9881054
Convert(child);
9891055
}
@@ -1010,6 +1076,11 @@ bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) {
10101076
return false;
10111077
}
10121078

1079+
bool Converter::VisitGotoStmt(clang::GotoStmt *stmt) {
1080+
StrCat(std::format("goto!('{})", stmt->getLabel()->getName().str()));
1081+
return false;
1082+
}
1083+
10131084
void Converter::ConvertCondition(clang::Expr *cond) {
10141085
PushExprKind push(*this, ExprKind::RValue);
10151086
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);
@@ -643,6 +649,24 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
643649

644650
std::unordered_set<const clang::VarDecl *> map_iter_decls_;
645651

652+
// Local variables hoisted outside a goto_block so that all labels can see and
653+
// use the variables.
654+
std::unordered_set<const clang::VarDecl *> hoisted_decls_;
655+
class PushHoistedDecls {
656+
public:
657+
PushHoistedDecls(std::unordered_set<const clang::VarDecl *> &field)
658+
: field_(field), saved_(std::move(field)) {
659+
field_.clear();
660+
}
661+
~PushHoistedDecls() { field_ = std::move(saved_); }
662+
PushHoistedDecls(const PushHoistedDecls &) = delete;
663+
PushHoistedDecls &operator=(const PushHoistedDecls &) = delete;
664+
665+
private:
666+
std::unordered_set<const clang::VarDecl *> &field_;
667+
std::unordered_set<const clang::VarDecl *> saved_;
668+
};
669+
646670
struct ScopedMapIterDecl {
647671
Converter &c;
648672
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)