Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,59 @@ bool Converter::VisitFunctionDecl(clang::FunctionDecl *decl) {
return false;
}

void Converter::EmitHoistedDecls(clang::CompoundStmt *body) {
for (auto *child : body->body()) {
if (auto *decl_stmt = clang::dyn_cast<clang::DeclStmt>(child)) {
for (auto *decl : decl_stmt->decls()) {
if (auto *var = clang::dyn_cast<clang::VarDecl>(decl);
var && var->isLocalVarDecl() && !IsGlobalVar(var)) {
hoisted_decls_.insert(var);
if (ConvertVarDeclSkipInit(var)) {
StrCat(token::kAssign, ConvertVarDefaultInit(var->getType()),
token::kSemiColon);
}
}
}
}
}
}

void Converter::ConvertGotoBlock(clang::CompoundStmt *body) {
PushHoistedDecls push(hoisted_decls_);
EmitHoistedDecls(body);

StrCat("goto_block!");
{
PushParen paren(*this);
PushBrace outer(*this);
StrCat("'__entry: ");
std::optional<PushBrace> arm;
arm.emplace(*this);
for (auto *child : body->body()) {
if (auto *label = clang::dyn_cast<clang::LabelStmt>(child)) {
arm.reset();
StrCat(std::format("'{}: ", label->getDecl()->getName().str()));
arm.emplace(*this);
Convert(label->getSubStmt());
} else {
Convert(child);
}
}
}
StrCat(token::kSemiColon);
}

void Converter::ConvertFunctionBody(clang::FunctionDecl *decl) {
if (auto compound = clang::dyn_cast<clang::CompoundStmt>(decl->getBody())) {
if (CompoundHasTopLevelLabel(compound)) {
ConvertGotoBlock(compound);
if (!decl->getReturnType()->isVoidType()) {
StrCat(R"(panic!("ub: non-void function does not return a value"))");
}
return;
}
}

Convert(decl->getBody());
if (!decl->getReturnType()->isVoidType()) {
if (auto compound = clang::dyn_cast<clang::CompoundStmt>(decl->getBody())) {
Expand Down Expand Up @@ -467,7 +519,20 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
}
}

void Converter::EmitHoistedInArmAssignment(clang::VarDecl *decl) {
if (!decl->hasInit()) {
return;
}
StrCat(GetNamedDeclAsString(decl), token::kAssign);
ConvertVarInit(decl->getType(), decl->getInit());
StrCat(token::kSemiColon);
}

void Converter::ConvertVarDecl(clang::VarDecl *decl) {
if (hoisted_decls_.contains(decl)) {
EmitHoistedInArmAssignment(decl);
return;
}
if (!ConvertVarDeclSkipInit(decl)) {
// Skip global variables declared extern
return;
Expand Down Expand Up @@ -985,6 +1050,10 @@ bool Converter::Convert(clang::Stmt *stmt) {
}

bool Converter::VisitCompoundStmt(clang::CompoundStmt *stmt) {
if (CompoundHasTopLevelLabel(stmt)) {
ConvertGotoBlock(stmt);
return false;
}
for (auto *child : stmt->body()) {
Convert(child);
}
Expand All @@ -1011,6 +1080,11 @@ bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) {
return false;
}

bool Converter::VisitGotoStmt(clang::GotoStmt *stmt) {
StrCat(std::format("goto!('{})", stmt->getLabel()->getName().str()));
return false;
}

void Converter::ConvertCondition(clang::Expr *cond) {
PushExprKind push(*this, ExprKind::RValue);
Convert(NormalizeToBool(cond, ctx_));
Expand Down
26 changes: 26 additions & 0 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,18 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

void ConvertGotoBlock(clang::CompoundStmt *body);

void EmitHoistedDecls(clang::CompoundStmt *body);

virtual bool VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl);

virtual bool VisitVarDecl(clang::VarDecl *decl);

void ConvertVarDecl(clang::VarDecl *decl);

virtual void EmitHoistedInArmAssignment(clang::VarDecl *decl);

void ConvertVarDeclInitializer(clang::VarDecl *decl);

virtual void ConvertGlobalVarDecl(clang::VarDecl *decl);
Expand Down Expand Up @@ -125,6 +131,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

virtual bool VisitGotoStmt(clang::GotoStmt *stmt);

void ConvertCondition(clang::Expr *cond);

virtual bool VisitIfStmt(clang::IfStmt *stmt);
Expand Down Expand Up @@ -646,6 +654,24 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

// Local variables hoisted outside a goto_block so that all labels can see and
// use the variables.
std::unordered_set<const clang::VarDecl *> hoisted_decls_;
class PushHoistedDecls {
public:
PushHoistedDecls(std::unordered_set<const clang::VarDecl *> &field)
: field_(field), saved_(std::move(field)) {
field_.clear();
}
~PushHoistedDecls() { field_ = std::move(saved_); }
PushHoistedDecls(const PushHoistedDecls &) = delete;
PushHoistedDecls &operator=(const PushHoistedDecls &) = delete;

private:
std::unordered_set<const clang::VarDecl *> &field_;
std::unordered_set<const clang::VarDecl *> saved_;
};

struct ScopedMapIterDecl {
Converter &c;
const clang::VarDecl *decl;
Expand Down
12 changes: 11 additions & 1 deletion cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,8 @@ static bool SwitchCaseHasFallthrough(clang::Stmt *stmt) {
}
if (clang::isa<clang::BreakStmt>(stmt) ||
clang::isa<clang::ContinueStmt>(stmt) ||
clang::isa<clang::ReturnStmt>(stmt)) {
clang::isa<clang::ReturnStmt>(stmt) ||
clang::isa<clang::GotoStmt>(stmt)) {
return false;
}
return true;
Expand All @@ -829,6 +830,15 @@ bool SwitchHasFallthrough(clang::SwitchStmt *stmt) {
return false;
}

bool CompoundHasTopLevelLabel(const clang::CompoundStmt *compound) {
for (const auto *child : compound->body()) {
if (clang::isa<clang::LabelStmt>(child)) {
return true;
}
}
return false;
}

std::string_view Trim(std::string_view s) {
auto is_space = [](unsigned char c) { return std::isspace(c); };
auto b = std::find_if_not(s.begin(), s.end(), is_space);
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ std::vector<clang::Stmt *> GetSwitchCaseBody(clang::CompoundStmt *body,

bool SwitchHasFallthrough(clang::SwitchStmt *stmt);

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

std::string_view Trim(std::string_view s);

void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix);
Expand Down
18 changes: 18 additions & 0 deletions cpp2rust/converter/models/converter_refcount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,24 @@ bool ConverterRefCount::ConvertLambdaVarDecl(clang::VarDecl *decl) {
return false;
}

bool ConverterRefCount::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
bool unboxed = in_function_formals_;
PushConversionKind push(*this, unboxed ? ConversionKind::Unboxed
: ConversionKind::FullRefCount);
return Converter::ConvertVarDeclSkipInit(decl);
}

void ConverterRefCount::EmitHoistedInArmAssignment(clang::VarDecl *decl) {
if (!decl->hasInit()) {
return;
}
PushConversionKind push(*this, ConversionKind::Unboxed);
StrCat(token::kStar, GetNamedDeclAsString(decl), ".borrow_mut()",
token::kAssign);
Convert(decl->getInit());
StrCat(token::kSemiColon);
}

void ConverterRefCount::ConvertGlobalVarDecl(clang::VarDecl *decl) {
StrCat("thread_local!");
{
Expand Down
4 changes: 4 additions & 0 deletions cpp2rust/converter/models/converter_refcount.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class ConverterRefCount final : public Converter {

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

bool ConvertVarDeclSkipInit(clang::VarDecl *decl) override;

void EmitHoistedInArmAssignment(clang::VarDecl *decl) override;

bool ConvertLambdaVarDecl(clang::VarDecl *decl) override;

bool VisitDeclRefExpr(clang::DeclRefExpr *expr) override;
Expand Down
31 changes: 31 additions & 0 deletions tests/unit/goto_aggregate_default.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <assert.h>
#include <stdio.h>

struct Point {
int x;
int y;
};

static int agg(int n) {
char buf40[40];
unsigned char buf256[256];
int arr64[64];
long longs[33];
struct Point p;
int *ptr;
int (*fp)(int);
FILE *file;
int total = 0;
if (n < 0) {
goto out;
}
total = 1;
out:
return total;
}

int main(void) {
assert(agg(-1) == 0);
assert(agg(1) == 1);
return 0;
}
18 changes: 18 additions & 0 deletions tests/unit/goto_backward.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <assert.h>

static int retry(int n) {
int count = 0;
int acc = 0;
again:
count += 1;
acc += n;
if (count < 3) {
goto again;
}
return acc;
}

int main(void) {
assert(retry(4) == 12);
return 0;
}
51 changes: 51 additions & 0 deletions tests/unit/goto_cleanup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <assert.h>

static int early(int n) {
int ret = 0;
if (n < 0) {
ret = -1;
goto out;
}
ret = 100;
out:
return ret;
}

static int from_loop(int n) {
int ret = 0;
for (int i = 0; i < n; i++) {
if (i == 3) {
ret = 7;
goto out;
}
ret += i;
}
ret = 999;
out:
return ret;
}

static int from_switch(int n) {
int ret = 0;
switch (n) {
case 1:
ret = 10;
goto out;
default:
ret = 20;
break;
}
ret = 999;
out:
return ret;
}

int main(void) {
assert(early(-1) == -1);
assert(early(5) == 100);
assert(from_loop(2) == 999);
assert(from_loop(10) == 7);
assert(from_switch(1) == 10);
assert(from_switch(2) == 999);
return 0;
}
22 changes: 22 additions & 0 deletions tests/unit/goto_loop_control.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <assert.h>

static int loopctl(void) {
int sum = 0;
for (int i = 0; i < 5; i++) {
if (i == 1) {
continue;
}
if (i == 4) {
break;
}
goto add;
add:
sum += 1;
}
return sum;
}

int main(void) {
assert(loopctl() == 3);
return 0;
}
24 changes: 24 additions & 0 deletions tests/unit/goto_multi_label.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <assert.h>

static int classify(int n) {
int ret = 0;
if (n < 0) {
goto error;
}
if (n == 0) {
goto out;
}
ret = n;
goto out;
error:
ret = -1;
out:
return ret;
}

int main(void) {
assert(classify(5) == 5);
assert(classify(0) == 0);
assert(classify(-2) == -1);
return 0;
}
Loading
Loading