diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8701a9b0..c60e9527 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -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(child)) { + for (auto *decl : decl_stmt->decls()) { + if (auto *var = clang::dyn_cast(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 arm; + arm.emplace(*this); + for (auto *child : body->body()) { + if (auto *label = clang::dyn_cast(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(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(decl->getBody())) { @@ -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; @@ -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); } @@ -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_)); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 1f0a113a..6292335c 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -82,12 +82,18 @@ class Converter : public clang::RecursiveASTVisitor { 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); @@ -125,6 +131,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitReturnStmt(clang::ReturnStmt *stmt); + virtual bool VisitGotoStmt(clang::GotoStmt *stmt); + void ConvertCondition(clang::Expr *cond); virtual bool VisitIfStmt(clang::IfStmt *stmt); @@ -646,6 +654,24 @@ class Converter : public clang::RecursiveASTVisitor { std::unordered_set map_iter_decls_; + // Local variables hoisted outside a goto_block so that all labels can see and + // use the variables. + std::unordered_set hoisted_decls_; + class PushHoistedDecls { + public: + PushHoistedDecls(std::unordered_set &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 &field_; + std::unordered_set saved_; + }; + struct ScopedMapIterDecl { Converter &c; const clang::VarDecl *decl; diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 2d5731c9..1e33c66f 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -811,7 +811,8 @@ static bool SwitchCaseHasFallthrough(clang::Stmt *stmt) { } if (clang::isa(stmt) || clang::isa(stmt) || - clang::isa(stmt)) { + clang::isa(stmt) || + clang::isa(stmt)) { return false; } return true; @@ -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(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); diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 6a6a70ec..015beb4b 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -169,6 +169,8 @@ std::vector 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); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index e3f3f353..2717a270 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -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!"); { diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index c4cc9056..5332115c 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -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; diff --git a/tests/unit/goto_aggregate_default.c b/tests/unit/goto_aggregate_default.c new file mode 100644 index 00000000..c6b7ef82 --- /dev/null +++ b/tests/unit/goto_aggregate_default.c @@ -0,0 +1,31 @@ +#include +#include + +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; +} diff --git a/tests/unit/goto_backward.c b/tests/unit/goto_backward.c new file mode 100644 index 00000000..d78780f1 --- /dev/null +++ b/tests/unit/goto_backward.c @@ -0,0 +1,18 @@ +#include + +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; +} diff --git a/tests/unit/goto_cleanup.c b/tests/unit/goto_cleanup.c new file mode 100644 index 00000000..ebb2891f --- /dev/null +++ b/tests/unit/goto_cleanup.c @@ -0,0 +1,51 @@ +#include + +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; +} diff --git a/tests/unit/goto_loop_control.c b/tests/unit/goto_loop_control.c new file mode 100644 index 00000000..617d18d0 --- /dev/null +++ b/tests/unit/goto_loop_control.c @@ -0,0 +1,22 @@ +#include + +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; +} diff --git a/tests/unit/goto_multi_label.c b/tests/unit/goto_multi_label.c new file mode 100644 index 00000000..471b7c99 --- /dev/null +++ b/tests/unit/goto_multi_label.c @@ -0,0 +1,24 @@ +#include + +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; +} diff --git a/tests/unit/goto_nested_label.c b/tests/unit/goto_nested_label.c new file mode 100644 index 00000000..9c5b78fc --- /dev/null +++ b/tests/unit/goto_nested_label.c @@ -0,0 +1,24 @@ +#include + +static int scan(int n) { + int total = 0; + for (int i = 0; i < n; i++) { + int j = 0; + while (j < 10) { + if (j == 5) { + goto next; + } + total += 1; + j++; + } + total += 100; + next: + total += 1000; + } + return total; +} + +int main(void) { + assert(scan(2) == 2010); + return 0; +} diff --git a/tests/unit/goto_switch_fallthrough.c b/tests/unit/goto_switch_fallthrough.c new file mode 100644 index 00000000..e0616c88 --- /dev/null +++ b/tests/unit/goto_switch_fallthrough.c @@ -0,0 +1,26 @@ +#include + +static int sm(int n) { + int ret = 0; + switch (n) { + case 0: + ret += 1; + /* fallthrough */ + case 1: + ret += 10; + goto out; + default: + ret += 100; + break; + } + ret += 1000; +out: + return ret; +} + +int main(void) { + assert(sm(0) == 11); + assert(sm(1) == 10); + assert(sm(9) == 1100); + return 0; +} diff --git a/tests/unit/out/refcount/goto_aggregate_default.rs b/tests/unit/out/refcount/goto_aggregate_default.rs new file mode 100644 index 00000000..d3cd131d --- /dev/null +++ b/tests/unit/out/refcount/goto_aggregate_default.rs @@ -0,0 +1,78 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive(Default)] +pub struct Point { + pub x: Value, + pub y: Value, +} +impl ByteRepr for Point { + fn to_bytes(&self, buf: &mut [u8]) { + (*self.x.borrow()).to_bytes(&mut buf[0..4]); + (*self.y.borrow()).to_bytes(&mut buf[4..8]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + x: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + y: Rc::new(RefCell::new(::from_bytes(&buf[4..8]))), + } + } +} +pub fn agg_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let buf40: Value> = Rc::new(RefCell::new( + (0..40).map(|_| ::default()).collect::>(), + )); + let buf256: Value> = Rc::new(RefCell::new( + (0..256).map(|_| ::default()).collect::>(), + )); + let arr64: Value> = Rc::new(RefCell::new( + (0..64).map(|_| ::default()).collect::>(), + )); + let longs: Value> = Rc::new(RefCell::new( + (0..33).map(|_| ::default()).collect::>(), + )); + let p: Value = >::default(); + let ptr: Value> = Rc::new(RefCell::new(Ptr::::null())); + let fp: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); + let file: Value> = Rc::new(RefCell::new(Ptr::null())); + let total: Value = >::default(); + goto_block!({ + '__entry: { + *total.borrow_mut() = 0; + if ((((*n.borrow()) < 0) as i32) != 0) { + goto!('out); + } + (*total.borrow_mut()) = 1; + } + 'out: { + return (*total.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = -1_i32; + agg_0(_n) + }) == 0) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 1; + agg_0(_n) + }) == 1) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/refcount/goto_backward.rs b/tests/unit/out/refcount/goto_backward.rs new file mode 100644 index 00000000..fbfe8a33 --- /dev/null +++ b/tests/unit/out/refcount/goto_backward.rs @@ -0,0 +1,41 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn retry_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let count: Value = >::default(); + let acc: Value = >::default(); + goto_block!({ + '__entry: { + *count.borrow_mut() = 0; + *acc.borrow_mut() = 0; + } + 'again: { + (*count.borrow_mut()) += 1; + (*acc.borrow_mut()) += (*n.borrow()); + if ((((*count.borrow()) < 3) as i32) != 0) { + goto!('again); + } + return (*acc.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = 4; + retry_0(_n) + }) == 12) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/refcount/goto_cleanup.rs b/tests/unit/out/refcount/goto_cleanup.rs new file mode 100644 index 00000000..a44fb20f --- /dev/null +++ b/tests/unit/out/refcount/goto_cleanup.rs @@ -0,0 +1,124 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn early_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ret: Value = >::default(); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + if ((((*n.borrow()) < 0) as i32) != 0) { + (*ret.borrow_mut()) = -1_i32; + goto!('out); + } + (*ret.borrow_mut()) = 100; + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn from_loop_1(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ret: Value = >::default(); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < (*n.borrow())) as i32) != 0) { + if ((((*i.borrow()) == 3) as i32) != 0) { + (*ret.borrow_mut()) = 7; + goto!('out); + } + (*ret.borrow_mut()) += (*i.borrow()); + (*i.borrow_mut()).postfix_inc(); + } + (*ret.borrow_mut()) = 999; + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn from_switch_2(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ret: Value = >::default(); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + 'switch: { + let __match_cond = (*n.borrow()); + match __match_cond { + v if v == 1 => { + (*ret.borrow_mut()) = 10; + goto!('out); + } + _ => { + (*ret.borrow_mut()) = 20; + break 'switch; + } + } + }; + (*ret.borrow_mut()) = 999; + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = -1_i32; + early_0(_n) + }) == -1_i32) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 5; + early_0(_n) + }) == 100) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 2; + from_loop_1(_n) + }) == 999) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 10; + from_loop_1(_n) + }) == 7) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 1; + from_switch_2(_n) + }) == 10) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 2; + from_switch_2(_n) + }) == 999) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/refcount/goto_loop_control.rs b/tests/unit/out/refcount/goto_loop_control.rs new file mode 100644 index 00000000..61e0486e --- /dev/null +++ b/tests/unit/out/refcount/goto_loop_control.rs @@ -0,0 +1,38 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn loopctl_0() -> i32 { + let sum: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < 5) as i32) != 0) { + goto_block!({ + '__entry: { + if ((((*i.borrow()) == 1) as i32) != 0) { + (*i.borrow_mut()).postfix_inc(); + continue 'loop_; + } + if ((((*i.borrow()) == 4) as i32) != 0) { + break; + } + goto!('add); + } + 'add: { + (*sum.borrow_mut()) += 1; + } + }); + (*i.borrow_mut()).postfix_inc(); + } + return (*sum.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!((((({ loopctl_0() }) == 3) as i32) != 0)); + return 0; +} diff --git a/tests/unit/out/refcount/goto_multi_label.rs b/tests/unit/out/refcount/goto_multi_label.rs new file mode 100644 index 00000000..4a3c74bf --- /dev/null +++ b/tests/unit/out/refcount/goto_multi_label.rs @@ -0,0 +1,59 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn classify_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ret: Value = >::default(); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + if ((((*n.borrow()) < 0) as i32) != 0) { + goto!('error); + } + if ((((*n.borrow()) == 0) as i32) != 0) { + goto!('out); + } + (*ret.borrow_mut()) = (*n.borrow()); + goto!('out); + } + 'error: { + (*ret.borrow_mut()) = -1_i32; + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = 5; + classify_0(_n) + }) == 5) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 0; + classify_0(_n) + }) == 0) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = -2_i32; + classify_0(_n) + }) == -1_i32) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/refcount/goto_nested_label.rs b/tests/unit/out/refcount/goto_nested_label.rs new file mode 100644 index 00000000..8dec8dd8 --- /dev/null +++ b/tests/unit/out/refcount/goto_nested_label.rs @@ -0,0 +1,47 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn scan_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let total: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < (*n.borrow())) as i32) != 0) { + let j: Value = >::default(); + goto_block!({ + '__entry: { + *j.borrow_mut() = 0; + 'loop_: while ((((*j.borrow()) < 10) as i32) != 0) { + if ((((*j.borrow()) == 5) as i32) != 0) { + goto!('next); + } + (*total.borrow_mut()) += 1; + (*j.borrow_mut()).postfix_inc(); + } + (*total.borrow_mut()) += 100; + } + 'next: { + (*total.borrow_mut()) += 1000; + } + }); + (*i.borrow_mut()).postfix_inc(); + } + return (*total.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = 2; + scan_0(_n) + }) == 2010) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/refcount/goto_switch_fallthrough.rs b/tests/unit/out/refcount/goto_switch_fallthrough.rs new file mode 100644 index 00000000..74581ceb --- /dev/null +++ b/tests/unit/out/refcount/goto_switch_fallthrough.rs @@ -0,0 +1,62 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn sm_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ret: Value = >::default(); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + switch!(match (*n.borrow()) { + v if v == 0 => { + (*ret.borrow_mut()) += 1; + } + v if v == 1 => { + (*ret.borrow_mut()) += 10; + goto!('out); + } + _ => { + (*ret.borrow_mut()) += 100; + break; + } + }); + (*ret.borrow_mut()) += 1000; + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = 0; + sm_0(_n) + }) == 11) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 1; + sm_0(_n) + }) == 10) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 9; + sm_0(_n) + }) == 1100) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_aggregate_default.rs b/tests/unit/out/unsafe/goto_aggregate_default.rs new file mode 100644 index 00000000..caca070a --- /dev/null +++ b/tests/unit/out/unsafe/goto_aggregate_default.rs @@ -0,0 +1,60 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Point { + pub x: i32, + pub y: i32, +} +pub unsafe fn agg_0(mut n: i32) -> i32 { + let mut buf40: [u8; 40] = [0_u8; 40]; + let mut buf256: [u8; 256] = [0_u8; 256]; + let mut arr64: [i32; 64] = [0_i32; 64]; + let mut longs: [i64; 33] = [0_i64; 33]; + let mut p: Point = ::default(); + let mut ptr: *mut i32 = std::ptr::null_mut(); + let mut fp: Option i32> = None; + let mut file: *mut ::libc::FILE = std::ptr::null_mut(); + let mut total: i32 = 0_i32; + goto_block!({ + '__entry: { + total = 0; + if ((((n) < (0)) as i32) != 0) { + goto!('out); + } + total = 1; + } + 'out: { + return total; + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((((unsafe { + let _n: i32 = -1_i32; + agg_0(_n) + }) == (0)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 1; + agg_0(_n) + }) == (1)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_backward.rs b/tests/unit/out/unsafe/goto_backward.rs new file mode 100644 index 00000000..312d7241 --- /dev/null +++ b/tests/unit/out/unsafe/goto_backward.rs @@ -0,0 +1,42 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn retry_0(mut n: i32) -> i32 { + let mut count: i32 = 0_i32; + let mut acc: i32 = 0_i32; + goto_block!({ + '__entry: { + count = 0; + acc = 0; + } + 'again: { + count += 1; + acc += n; + if ((((count) < (3)) as i32) != 0) { + goto!('again); + } + return acc; + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((((unsafe { + let _n: i32 = 4; + retry_0(_n) + }) == (12)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_cleanup.rs b/tests/unit/out/unsafe/goto_cleanup.rs new file mode 100644 index 00000000..b4bcb361 --- /dev/null +++ b/tests/unit/out/unsafe/goto_cleanup.rs @@ -0,0 +1,123 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn early_0(mut n: i32) -> i32 { + let mut ret: i32 = 0_i32; + goto_block!({ + '__entry: { + ret = 0; + if ((((n) < (0)) as i32) != 0) { + ret = -1_i32; + goto!('out); + } + ret = 100; + } + 'out: { + return ret; + } + }); + panic!("ub: non-void function does not return a value") +} +pub unsafe fn from_loop_1(mut n: i32) -> i32 { + let mut ret: i32 = 0_i32; + goto_block!({ + '__entry: { + ret = 0; + let mut i: i32 = 0; + 'loop_: while ((((i) < (n)) as i32) != 0) { + if ((((i) == (3)) as i32) != 0) { + ret = 7; + goto!('out); + } + ret += i; + i.postfix_inc(); + } + ret = 999; + } + 'out: { + return ret; + } + }); + panic!("ub: non-void function does not return a value") +} +pub unsafe fn from_switch_2(mut n: i32) -> i32 { + let mut ret: i32 = 0_i32; + goto_block!({ + '__entry: { + ret = 0; + 'switch: { + let __match_cond = n; + match __match_cond { + v if v == 1 => { + ret = 10; + goto!('out); + } + _ => { + ret = 20; + break 'switch; + } + } + }; + ret = 999; + } + 'out: { + return ret; + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((((unsafe { + let _n: i32 = -1_i32; + early_0(_n) + }) == (-1_i32)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 5; + early_0(_n) + }) == (100)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 2; + from_loop_1(_n) + }) == (999)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 10; + from_loop_1(_n) + }) == (7)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 1; + from_switch_2(_n) + }) == (10)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 2; + from_switch_2(_n) + }) == (999)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_loop_control.rs b/tests/unit/out/unsafe/goto_loop_control.rs new file mode 100644 index 00000000..e0d21867 --- /dev/null +++ b/tests/unit/out/unsafe/goto_loop_control.rs @@ -0,0 +1,40 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn loopctl_0() -> i32 { + let mut sum: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((((i) < (5)) as i32) != 0) { + goto_block!({ + '__entry: { + if ((((i) == (1)) as i32) != 0) { + i.postfix_inc(); + continue 'loop_; + } + if ((((i) == (4)) as i32) != 0) { + break; + } + goto!('add); + } + 'add: { + sum += 1; + } + }); + i.postfix_inc(); + } + return sum; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!(((((unsafe { loopctl_0() }) == (3)) as i32) != 0)); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_multi_label.rs b/tests/unit/out/unsafe/goto_multi_label.rs new file mode 100644 index 00000000..518fa85c --- /dev/null +++ b/tests/unit/out/unsafe/goto_multi_label.rs @@ -0,0 +1,60 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn classify_0(mut n: i32) -> i32 { + let mut ret: i32 = 0_i32; + goto_block!({ + '__entry: { + ret = 0; + if ((((n) < (0)) as i32) != 0) { + goto!('error); + } + if ((((n) == (0)) as i32) != 0) { + goto!('out); + } + ret = n; + goto!('out); + } + 'error: { + ret = -1_i32; + } + 'out: { + return ret; + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((((unsafe { + let _n: i32 = 5; + classify_0(_n) + }) == (5)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 0; + classify_0(_n) + }) == (0)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = -2_i32; + classify_0(_n) + }) == (-1_i32)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_nested_label.rs b/tests/unit/out/unsafe/goto_nested_label.rs new file mode 100644 index 00000000..aeb474c8 --- /dev/null +++ b/tests/unit/out/unsafe/goto_nested_label.rs @@ -0,0 +1,48 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn scan_0(mut n: i32) -> i32 { + let mut total: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((((i) < (n)) as i32) != 0) { + let mut j: i32 = 0_i32; + goto_block!({ + '__entry: { + j = 0; + 'loop_: while ((((j) < (10)) as i32) != 0) { + if ((((j) == (5)) as i32) != 0) { + goto!('next); + } + total += 1; + j.postfix_inc(); + } + total += 100; + } + 'next: { + total += 1000; + } + }); + i.postfix_inc(); + } + return total; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((((unsafe { + let _n: i32 = 2; + scan_0(_n) + }) == (2010)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/goto_switch_fallthrough.rs b/tests/unit/out/unsafe/goto_switch_fallthrough.rs new file mode 100644 index 00000000..af0011cd --- /dev/null +++ b/tests/unit/out/unsafe/goto_switch_fallthrough.rs @@ -0,0 +1,63 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn sm_0(mut n: i32) -> i32 { + let mut ret: i32 = 0_i32; + goto_block!({ + '__entry: { + ret = 0; + switch!(match n { + v if v == 0 => { + ret += 1; + } + v if v == 1 => { + ret += 10; + goto!('out); + } + _ => { + ret += 100; + break; + } + }); + ret += 1000; + } + 'out: { + return ret; + } + }); + panic!("ub: non-void function does not return a value") +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((((unsafe { + let _n: i32 = 0; + sm_0(_n) + }) == (11)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 1; + sm_0(_n) + }) == (10)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 9; + sm_0(_n) + }) == (1100)) as i32) + != 0) + ); + return 0; +}