From 0b7b69af64d3edc2a3886cfd3143dfdaaad2da25 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 29 May 2026 14:48:57 +0100 Subject: [PATCH 1/4] Add more goto tests and their expected output --- tests/unit/goto_aggregate_default.c | 22 ++++ tests/unit/goto_backward.c | 18 +++ tests/unit/goto_cleanup.c | 51 +++++++ tests/unit/goto_forward_skip.c | 18 +++ tests/unit/goto_loop_control.c | 25 ++++ tests/unit/goto_multi_label.c | 24 ++++ tests/unit/goto_nested_label.c | 24 ++++ tests/unit/goto_switch_exit.c | 26 ++++ tests/unit/goto_switch_fallthrough.c | 26 ++++ .../out/refcount/goto_aggregate_default.rs | 58 ++++++++ tests/unit/out/refcount/goto_backward.rs | 41 ++++++ tests/unit/out/refcount/goto_cleanup.rs | 124 ++++++++++++++++++ tests/unit/out/refcount/goto_forward_skip.rs | 46 +++++++ tests/unit/out/refcount/goto_loop_control.rs | 48 +++++++ tests/unit/out/refcount/goto_multi_label.rs | 59 +++++++++ tests/unit/out/refcount/goto_nested_label.rs | 47 +++++++ tests/unit/out/refcount/goto_switch_exit.rs | 66 ++++++++++ .../out/refcount/goto_switch_fallthrough.rs | 62 +++++++++ .../unit/out/unsafe/goto_aggregate_default.rs | 58 ++++++++ tests/unit/out/unsafe/goto_backward.rs | 42 ++++++ tests/unit/out/unsafe/goto_cleanup.rs | 123 +++++++++++++++++ tests/unit/out/unsafe/goto_forward_skip.rs | 47 +++++++ tests/unit/out/unsafe/goto_loop_control.rs | 49 +++++++ tests/unit/out/unsafe/goto_multi_label.rs | 60 +++++++++ tests/unit/out/unsafe/goto_nested_label.rs | 48 +++++++ tests/unit/out/unsafe/goto_switch_exit.rs | 67 ++++++++++ .../out/unsafe/goto_switch_fallthrough.rs | 63 +++++++++ 27 files changed, 1342 insertions(+) create mode 100644 tests/unit/goto_aggregate_default.c create mode 100644 tests/unit/goto_backward.c create mode 100644 tests/unit/goto_cleanup.c create mode 100644 tests/unit/goto_forward_skip.c create mode 100644 tests/unit/goto_loop_control.c create mode 100644 tests/unit/goto_multi_label.c create mode 100644 tests/unit/goto_nested_label.c create mode 100644 tests/unit/goto_switch_exit.c create mode 100644 tests/unit/goto_switch_fallthrough.c create mode 100644 tests/unit/out/refcount/goto_aggregate_default.rs create mode 100644 tests/unit/out/refcount/goto_backward.rs create mode 100644 tests/unit/out/refcount/goto_cleanup.rs create mode 100644 tests/unit/out/refcount/goto_forward_skip.rs create mode 100644 tests/unit/out/refcount/goto_loop_control.rs create mode 100644 tests/unit/out/refcount/goto_multi_label.rs create mode 100644 tests/unit/out/refcount/goto_nested_label.rs create mode 100644 tests/unit/out/refcount/goto_switch_exit.rs create mode 100644 tests/unit/out/refcount/goto_switch_fallthrough.rs create mode 100644 tests/unit/out/unsafe/goto_aggregate_default.rs create mode 100644 tests/unit/out/unsafe/goto_backward.rs create mode 100644 tests/unit/out/unsafe/goto_cleanup.rs create mode 100644 tests/unit/out/unsafe/goto_forward_skip.rs create mode 100644 tests/unit/out/unsafe/goto_loop_control.rs create mode 100644 tests/unit/out/unsafe/goto_multi_label.rs create mode 100644 tests/unit/out/unsafe/goto_nested_label.rs create mode 100644 tests/unit/out/unsafe/goto_switch_exit.rs create mode 100644 tests/unit/out/unsafe/goto_switch_fallthrough.rs diff --git a/tests/unit/goto_aggregate_default.c b/tests/unit/goto_aggregate_default.c new file mode 100644 index 00000000..17832503 --- /dev/null +++ b/tests/unit/goto_aggregate_default.c @@ -0,0 +1,22 @@ +#include +#include + +static int agg(int n) { + char buf[40]; + int total = 0; + if (n < 0) { + goto out; + } + memset(buf, 1, sizeof(buf)); + for (int i = 0; i < 40; i++) { + total += buf[i]; + } +out: + return total; +} + +int main(void) { + assert(agg(-1) == 0); + assert(agg(2) == 40); + 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_forward_skip.c b/tests/unit/goto_forward_skip.c new file mode 100644 index 00000000..55cdfd0d --- /dev/null +++ b/tests/unit/goto_forward_skip.c @@ -0,0 +1,18 @@ +#include + +static int skip(int n) { + int x = 0; + if (n > 0) { + goto mid; + } + x += 10; +mid: + x += 1; + return x; +} + +int main(void) { + assert(skip(1) == 1); + assert(skip(-1) == 11); + return 0; +} diff --git a/tests/unit/goto_loop_control.c b/tests/unit/goto_loop_control.c new file mode 100644 index 00000000..e74caaaa --- /dev/null +++ b/tests/unit/goto_loop_control.c @@ -0,0 +1,25 @@ +#include + +static int loopctl(int n) { + int total = 0; + for (int i = 0; i < n; i++) { + if (i == 2) { + continue; + } + if (i == 5) { + break; + } + if (i % 2 == 0) { + goto even; + } + total += 1; + even: + total += 10; + } + return total; +} + +int main(void) { + assert(loopctl(10) == 42); + 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_exit.c b/tests/unit/goto_switch_exit.c new file mode 100644 index 00000000..a550dd5c --- /dev/null +++ b/tests/unit/goto_switch_exit.c @@ -0,0 +1,26 @@ +#include + +static int classify(int n) { + int ret = 0; + switch (n) { + case 1: + ret = 10; + goto out; + case 2: + ret = 20; + break; + default: + ret = 30; + break; + } + ret += 1; +out: + return ret; +} + +int main(void) { + assert(classify(1) == 10); + assert(classify(2) == 21); + assert(classify(9) == 31); + 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..05a89b34 --- /dev/null +++ b/tests/unit/out/refcount/goto_aggregate_default.rs @@ -0,0 +1,58 @@ +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 agg_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let mut buf: Value> = Rc::new(RefCell::new( + (0..40).map(|_| ::default()).collect::>(), + )); + let mut total: Value = >::default(); + goto_block!({ + '__entry: { + total = Rc::new(RefCell::new(0)); + if ((((*n.borrow()) < 0) as i32) != 0) { + goto!('out); + } + { + ((buf.as_pointer() as Ptr) as Ptr) + .to_any() + .memset((1) as u8, ::std::mem::size_of::<[u8; 40]>() as u64 as usize); + ((buf.as_pointer() as Ptr) as Ptr).to_any().clone() + }; + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < 40) as i32) != 0) { + (*total.borrow_mut()) += ((*buf.borrow())[(*i.borrow()) as usize] as i32); + (*i.borrow_mut()).postfix_inc(); + } + } + '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 = 2; + agg_0(_n) + }) == 40) 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..c67f0836 --- /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 mut count: Value = >::default(); + let mut acc: Value = >::default(); + goto_block!({ + '__entry: { + count = Rc::new(RefCell::new(0)); + acc = Rc::new(RefCell::new(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..86928b1e --- /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 mut ret: Value = >::default(); + goto_block!({ + '__entry: { + ret = Rc::new(RefCell::new(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 mut ret: Value = >::default(); + goto_block!({ + '__entry: { + ret = Rc::new(RefCell::new(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 mut ret: Value = >::default(); + goto_block!({ + '__entry: { + ret = Rc::new(RefCell::new(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_forward_skip.rs b/tests/unit/out/refcount/goto_forward_skip.rs new file mode 100644 index 00000000..e5b40898 --- /dev/null +++ b/tests/unit/out/refcount/goto_forward_skip.rs @@ -0,0 +1,46 @@ +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 skip_0(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let mut x: Value = >::default(); + goto_block!({ + '__entry: { + x = Rc::new(RefCell::new(0)); + if ((((*n.borrow()) > 0) as i32) != 0) { + goto!('mid); + } + (*x.borrow_mut()) += 10; + } + 'mid: { + (*x.borrow_mut()) += 1; + return (*x.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; + skip_0(_n) + }) == 1) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = -1_i32; + skip_0(_n) + }) == 11) 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..9ad06970 --- /dev/null +++ b/tests/unit/out/refcount/goto_loop_control.rs @@ -0,0 +1,48 @@ +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(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) { + goto_block!({ + '__entry: { + if ((((*i.borrow()) == 2) as i32) != 0) { + (*i.borrow_mut()).postfix_inc(); + continue 'loop_; + } + if ((((*i.borrow()) == 5) as i32) != 0) { + break; + } + if (((((*i.borrow()) % 2) == 0) as i32) != 0) { + goto!('even); + } + (*total.borrow_mut()) += 1; + } + 'even: { + (*total.borrow_mut()) += 10; + } + }); + (*i.borrow_mut()).postfix_inc(); + } + return (*total.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (((({ + let _n: i32 = 10; + loopctl_0(_n) + }) == 42) 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..721e7940 --- /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 mut ret: Value = >::default(); + goto_block!({ + '__entry: { + ret = Rc::new(RefCell::new(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..7a9fb7f9 --- /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 mut j: Value = >::default(); + goto_block!({ + '__entry: { + j = Rc::new(RefCell::new(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_exit.rs b/tests/unit/out/refcount/goto_switch_exit.rs new file mode 100644 index 00000000..bf1fafb8 --- /dev/null +++ b/tests/unit/out/refcount/goto_switch_exit.rs @@ -0,0 +1,66 @@ +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 mut ret: Value = >::default(); + goto_block!({ + '__entry: { + ret = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*n.borrow()); + match __match_cond { + v if v == 1 => { + (*ret.borrow_mut()) = 10; + goto!('out); + } + v if v == 2 => { + (*ret.borrow_mut()) = 20; + break 'switch; + } + _ => { + (*ret.borrow_mut()) = 30; + break 'switch; + } + } + }; + (*ret.borrow_mut()) += 1; + } + '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; + classify_0(_n) + }) == 10) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 2; + classify_0(_n) + }) == 21) as i32) + != 0) + ); + assert!( + (((({ + let _n: i32 = 9; + classify_0(_n) + }) == 31) 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..469c67ce --- /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 mut ret: Value = >::default(); + goto_block!({ + '__entry: { + ret = Rc::new(RefCell::new(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..8c13bead --- /dev/null +++ b/tests/unit/out/unsafe/goto_aggregate_default.rs @@ -0,0 +1,58 @@ +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 agg_0(mut n: i32) -> i32 { + let mut buf: [u8; 40] = [0_u8; 40]; + let mut total: i32 = 0_i32; + goto_block!({ + '__entry: { + total = 0; + if ((((n) < (0)) as i32) != 0) { + goto!('out); + } + { + let byte_0 = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut u8; + for offset in 0..::std::mem::size_of::<[u8; 40]>() as u64 { + *byte_0.offset(offset as isize) = 1 as u8; + } + (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) + }; + let mut i: i32 = 0; + 'loop_: while ((((i) < (40)) as i32) != 0) { + total += (buf[(i) as usize] as i32); + i.postfix_inc(); + } + } + '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 = 2; + agg_0(_n) + }) == (40)) 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_forward_skip.rs b/tests/unit/out/unsafe/goto_forward_skip.rs new file mode 100644 index 00000000..8192767a --- /dev/null +++ b/tests/unit/out/unsafe/goto_forward_skip.rs @@ -0,0 +1,47 @@ +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 skip_0(mut n: i32) -> i32 { + let mut x: i32 = 0_i32; + goto_block!({ + '__entry: { + x = 0; + if ((((n) > (0)) as i32) != 0) { + goto!('mid); + } + x += 10; + } + 'mid: { + x += 1; + return x; + } + }); + 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; + skip_0(_n) + }) == (1)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = -1_i32; + skip_0(_n) + }) == (11)) 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..7b5b8415 --- /dev/null +++ b/tests/unit/out/unsafe/goto_loop_control.rs @@ -0,0 +1,49 @@ +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(mut n: i32) -> i32 { + let mut total: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((((i) < (n)) as i32) != 0) { + goto_block!({ + '__entry: { + if ((((i) == (2)) as i32) != 0) { + i.postfix_inc(); + continue 'loop_; + } + if ((((i) == (5)) as i32) != 0) { + break; + } + if (((((i) % (2)) == (0)) as i32) != 0) { + goto!('even); + } + total += 1; + } + 'even: { + total += 10; + } + }); + 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 = 10; + loopctl_0(_n) + }) == (42)) 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_exit.rs b/tests/unit/out/unsafe/goto_switch_exit.rs new file mode 100644 index 00000000..fc1bbaeb --- /dev/null +++ b/tests/unit/out/unsafe/goto_switch_exit.rs @@ -0,0 +1,67 @@ +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; + 'switch: { + let __match_cond = n; + match __match_cond { + v if v == 1 => { + ret = 10; + goto!('out); + } + v if v == 2 => { + ret = 20; + break 'switch; + } + _ => { + ret = 30; + break 'switch; + } + } + }; + ret += 1; + } + '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; + classify_0(_n) + }) == (10)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 2; + classify_0(_n) + }) == (21)) as i32) + != 0) + ); + assert!( + ((((unsafe { + let _n: i32 = 9; + classify_0(_n) + }) == (31)) 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; +} From 92979cdb0a508df21cb4865231a7a2f6f918561b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 1 Jun 2026 13:04:06 +0100 Subject: [PATCH 2/4] Add codegen support for goto --- cpp2rust/converter/converter.cpp | 77 ++++++++++++++++++- cpp2rust/converter/converter.h | 24 ++++++ cpp2rust/converter/converter_lib.cpp | 12 ++- cpp2rust/converter/converter_lib.h | 2 + .../converter/models/converter_refcount.cpp | 7 ++ .../converter/models/converter_refcount.h | 2 + 6 files changed, 120 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8701a9b0..6f617631 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())) { @@ -422,9 +474,11 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { auto *method_or_null = curr_function_ ? clang::dyn_cast(curr_function_) : nullptr; - if (!qual_type.isConstQualified() && !qual_type->isReferenceType() && - ((method_or_null == nullptr) || !method_or_null->isVirtual()) && - !IsGlobalVar(decl)) { + if (hoisted_decls_.contains(decl) && !qual_type->isReferenceType()) { + StrCat("mut"); + } else if (!qual_type.isConstQualified() && !qual_type->isReferenceType() && + ((method_or_null == nullptr) || !method_or_null->isVirtual()) && + !IsGlobalVar(decl)) { StrCat(keyword_mut_); } @@ -468,6 +522,14 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) { } void Converter::ConvertVarDecl(clang::VarDecl *decl) { + if (hoisted_decls_.contains(decl)) { + if (decl->hasInit()) { + StrCat(GetNamedDeclAsString(decl), token::kAssign); + ConvertVarInit(decl->getType(), decl->getInit()); + StrCat(token::kSemiColon); + } + return; + } if (!ConvertVarDeclSkipInit(decl)) { // Skip global variables declared extern return; @@ -985,6 +1047,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 +1077,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..1a629801 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -82,6 +82,10 @@ 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); @@ -125,6 +129,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 +652,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..929618b4 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -621,6 +621,13 @@ 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::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..131847e5 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -57,6 +57,8 @@ class ConverterRefCount final : public Converter { void ConvertVaListVarDecl(clang::VarDecl *decl) override; + bool ConvertVarDeclSkipInit(clang::VarDecl *decl) override; + bool ConvertLambdaVarDecl(clang::VarDecl *decl) override; bool VisitDeclRefExpr(clang::DeclRefExpr *expr) override; From e8d956fb7fa1db62f7dd475104c79267e33d78ed Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 1 Jun 2026 13:14:45 +0100 Subject: [PATCH 3/4] Update tests --- tests/unit/goto_aggregate_default.c | 23 +++++-- tests/unit/goto_forward_skip.c | 18 ----- tests/unit/goto_loop_control.c | 23 +++---- tests/unit/goto_switch_exit.c | 26 ------- .../out/refcount/goto_aggregate_default.rs | 48 +++++++++---- tests/unit/out/refcount/goto_forward_skip.rs | 46 ------------- tests/unit/out/refcount/goto_loop_control.rs | 30 +++------ tests/unit/out/refcount/goto_switch_exit.rs | 66 ------------------ .../unit/out/unsafe/goto_aggregate_default.rs | 32 ++++----- tests/unit/out/unsafe/goto_forward_skip.rs | 47 ------------- tests/unit/out/unsafe/goto_loop_control.rs | 29 +++----- tests/unit/out/unsafe/goto_switch_exit.rs | 67 ------------------- 12 files changed, 97 insertions(+), 358 deletions(-) delete mode 100644 tests/unit/goto_forward_skip.c delete mode 100644 tests/unit/goto_switch_exit.c delete mode 100644 tests/unit/out/refcount/goto_forward_skip.rs delete mode 100644 tests/unit/out/refcount/goto_switch_exit.rs delete mode 100644 tests/unit/out/unsafe/goto_forward_skip.rs delete mode 100644 tests/unit/out/unsafe/goto_switch_exit.rs diff --git a/tests/unit/goto_aggregate_default.c b/tests/unit/goto_aggregate_default.c index 17832503..c6b7ef82 100644 --- a/tests/unit/goto_aggregate_default.c +++ b/tests/unit/goto_aggregate_default.c @@ -1,22 +1,31 @@ #include -#include +#include + +struct Point { + int x; + int y; +}; static int agg(int n) { - char buf[40]; + 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; } - memset(buf, 1, sizeof(buf)); - for (int i = 0; i < 40; i++) { - total += buf[i]; - } + total = 1; out: return total; } int main(void) { assert(agg(-1) == 0); - assert(agg(2) == 40); + assert(agg(1) == 1); return 0; } diff --git a/tests/unit/goto_forward_skip.c b/tests/unit/goto_forward_skip.c deleted file mode 100644 index 55cdfd0d..00000000 --- a/tests/unit/goto_forward_skip.c +++ /dev/null @@ -1,18 +0,0 @@ -#include - -static int skip(int n) { - int x = 0; - if (n > 0) { - goto mid; - } - x += 10; -mid: - x += 1; - return x; -} - -int main(void) { - assert(skip(1) == 1); - assert(skip(-1) == 11); - return 0; -} diff --git a/tests/unit/goto_loop_control.c b/tests/unit/goto_loop_control.c index e74caaaa..617d18d0 100644 --- a/tests/unit/goto_loop_control.c +++ b/tests/unit/goto_loop_control.c @@ -1,25 +1,22 @@ #include -static int loopctl(int n) { - int total = 0; - for (int i = 0; i < n; i++) { - if (i == 2) { +static int loopctl(void) { + int sum = 0; + for (int i = 0; i < 5; i++) { + if (i == 1) { continue; } - if (i == 5) { + if (i == 4) { break; } - if (i % 2 == 0) { - goto even; - } - total += 1; - even: - total += 10; + goto add; + add: + sum += 1; } - return total; + return sum; } int main(void) { - assert(loopctl(10) == 42); + assert(loopctl() == 3); return 0; } diff --git a/tests/unit/goto_switch_exit.c b/tests/unit/goto_switch_exit.c deleted file mode 100644 index a550dd5c..00000000 --- a/tests/unit/goto_switch_exit.c +++ /dev/null @@ -1,26 +0,0 @@ -#include - -static int classify(int n) { - int ret = 0; - switch (n) { - case 1: - ret = 10; - goto out; - case 2: - ret = 20; - break; - default: - ret = 30; - break; - } - ret += 1; -out: - return ret; -} - -int main(void) { - assert(classify(1) == 10); - assert(classify(2) == 21); - assert(classify(9) == 31); - return 0; -} diff --git a/tests/unit/out/refcount/goto_aggregate_default.rs b/tests/unit/out/refcount/goto_aggregate_default.rs index 05a89b34..004f2355 100644 --- a/tests/unit/out/refcount/goto_aggregate_default.rs +++ b/tests/unit/out/refcount/goto_aggregate_default.rs @@ -6,11 +6,41 @@ 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 mut buf: Value> = Rc::new(RefCell::new( + let mut buf40: Value> = Rc::new(RefCell::new( (0..40).map(|_| ::default()).collect::>(), )); + let mut buf256: Value> = Rc::new(RefCell::new( + (0..256).map(|_| ::default()).collect::>(), + )); + let mut arr64: Value> = Rc::new(RefCell::new( + (0..64).map(|_| ::default()).collect::>(), + )); + let mut longs: Value> = Rc::new(RefCell::new( + (0..33).map(|_| ::default()).collect::>(), + )); + let mut p: Value = >::default(); + let mut ptr: Value> = Rc::new(RefCell::new(Ptr::::null())); + let mut fp: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); + let mut file: Value> = Rc::new(RefCell::new(Ptr::null())); let mut total: Value = >::default(); goto_block!({ '__entry: { @@ -18,17 +48,7 @@ pub fn agg_0(n: i32) -> i32 { if ((((*n.borrow()) < 0) as i32) != 0) { goto!('out); } - { - ((buf.as_pointer() as Ptr) as Ptr) - .to_any() - .memset((1) as u8, ::std::mem::size_of::<[u8; 40]>() as u64 as usize); - ((buf.as_pointer() as Ptr) as Ptr).to_any().clone() - }; - let i: Value = Rc::new(RefCell::new(0)); - 'loop_: while ((((*i.borrow()) < 40) as i32) != 0) { - (*total.borrow_mut()) += ((*buf.borrow())[(*i.borrow()) as usize] as i32); - (*i.borrow_mut()).postfix_inc(); - } + (*total.borrow_mut()) = 1; } 'out: { return (*total.borrow()); @@ -49,9 +69,9 @@ fn main_0() -> i32 { ); assert!( (((({ - let _n: i32 = 2; + let _n: i32 = 1; agg_0(_n) - }) == 40) as i32) + }) == 1) as i32) != 0) ); return 0; diff --git a/tests/unit/out/refcount/goto_forward_skip.rs b/tests/unit/out/refcount/goto_forward_skip.rs deleted file mode 100644 index e5b40898..00000000 --- a/tests/unit/out/refcount/goto_forward_skip.rs +++ /dev/null @@ -1,46 +0,0 @@ -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 skip_0(n: i32) -> i32 { - let n: Value = Rc::new(RefCell::new(n)); - let mut x: Value = >::default(); - goto_block!({ - '__entry: { - x = Rc::new(RefCell::new(0)); - if ((((*n.borrow()) > 0) as i32) != 0) { - goto!('mid); - } - (*x.borrow_mut()) += 10; - } - 'mid: { - (*x.borrow_mut()) += 1; - return (*x.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; - skip_0(_n) - }) == 1) as i32) - != 0) - ); - assert!( - (((({ - let _n: i32 = -1_i32; - skip_0(_n) - }) == 11) 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 index 9ad06970..61e0486e 100644 --- a/tests/unit/out/refcount/goto_loop_control.rs +++ b/tests/unit/out/refcount/goto_loop_control.rs @@ -6,43 +6,33 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -pub fn loopctl_0(n: i32) -> i32 { - let n: Value = Rc::new(RefCell::new(n)); - let total: Value = Rc::new(RefCell::new(0)); +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()) < (*n.borrow())) as i32) != 0) { + 'loop_: while ((((*i.borrow()) < 5) as i32) != 0) { goto_block!({ '__entry: { - if ((((*i.borrow()) == 2) as i32) != 0) { + if ((((*i.borrow()) == 1) as i32) != 0) { (*i.borrow_mut()).postfix_inc(); continue 'loop_; } - if ((((*i.borrow()) == 5) as i32) != 0) { + if ((((*i.borrow()) == 4) as i32) != 0) { break; } - if (((((*i.borrow()) % 2) == 0) as i32) != 0) { - goto!('even); - } - (*total.borrow_mut()) += 1; + goto!('add); } - 'even: { - (*total.borrow_mut()) += 10; + 'add: { + (*sum.borrow_mut()) += 1; } }); (*i.borrow_mut()).postfix_inc(); } - return (*total.borrow()); + return (*sum.borrow()); } pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - assert!( - (((({ - let _n: i32 = 10; - loopctl_0(_n) - }) == 42) as i32) - != 0) - ); + assert!((((({ loopctl_0() }) == 3) as i32) != 0)); return 0; } diff --git a/tests/unit/out/refcount/goto_switch_exit.rs b/tests/unit/out/refcount/goto_switch_exit.rs deleted file mode 100644 index bf1fafb8..00000000 --- a/tests/unit/out/refcount/goto_switch_exit.rs +++ /dev/null @@ -1,66 +0,0 @@ -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 mut ret: Value = >::default(); - goto_block!({ - '__entry: { - ret = Rc::new(RefCell::new(0)); - 'switch: { - let __match_cond = (*n.borrow()); - match __match_cond { - v if v == 1 => { - (*ret.borrow_mut()) = 10; - goto!('out); - } - v if v == 2 => { - (*ret.borrow_mut()) = 20; - break 'switch; - } - _ => { - (*ret.borrow_mut()) = 30; - break 'switch; - } - } - }; - (*ret.borrow_mut()) += 1; - } - '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; - classify_0(_n) - }) == 10) as i32) - != 0) - ); - assert!( - (((({ - let _n: i32 = 2; - classify_0(_n) - }) == 21) as i32) - != 0) - ); - assert!( - (((({ - let _n: i32 = 9; - classify_0(_n) - }) == 31) 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 index 8c13bead..caca070a 100644 --- a/tests/unit/out/unsafe/goto_aggregate_default.rs +++ b/tests/unit/out/unsafe/goto_aggregate_default.rs @@ -6,8 +6,21 @@ 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 buf: [u8; 40] = [0_u8; 40]; + 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: { @@ -15,18 +28,7 @@ pub unsafe fn agg_0(mut n: i32) -> i32 { if ((((n) < (0)) as i32) != 0) { goto!('out); } - { - let byte_0 = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut u8; - for offset in 0..::std::mem::size_of::<[u8; 40]>() as u64 { - *byte_0.offset(offset as isize) = 1 as u8; - } - (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) - }; - let mut i: i32 = 0; - 'loop_: while ((((i) < (40)) as i32) != 0) { - total += (buf[(i) as usize] as i32); - i.postfix_inc(); - } + total = 1; } 'out: { return total; @@ -49,9 +51,9 @@ unsafe fn main_0() -> i32 { ); assert!( ((((unsafe { - let _n: i32 = 2; + let _n: i32 = 1; agg_0(_n) - }) == (40)) as i32) + }) == (1)) as i32) != 0) ); return 0; diff --git a/tests/unit/out/unsafe/goto_forward_skip.rs b/tests/unit/out/unsafe/goto_forward_skip.rs deleted file mode 100644 index 8192767a..00000000 --- a/tests/unit/out/unsafe/goto_forward_skip.rs +++ /dev/null @@ -1,47 +0,0 @@ -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 skip_0(mut n: i32) -> i32 { - let mut x: i32 = 0_i32; - goto_block!({ - '__entry: { - x = 0; - if ((((n) > (0)) as i32) != 0) { - goto!('mid); - } - x += 10; - } - 'mid: { - x += 1; - return x; - } - }); - 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; - skip_0(_n) - }) == (1)) as i32) - != 0) - ); - assert!( - ((((unsafe { - let _n: i32 = -1_i32; - skip_0(_n) - }) == (11)) 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 index 7b5b8415..e0d21867 100644 --- a/tests/unit/out/unsafe/goto_loop_control.rs +++ b/tests/unit/out/unsafe/goto_loop_control.rs @@ -6,31 +6,28 @@ 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(mut n: i32) -> i32 { - let mut total: i32 = 0; +pub unsafe fn loopctl_0() -> i32 { + let mut sum: i32 = 0; let mut i: i32 = 0; - 'loop_: while ((((i) < (n)) as i32) != 0) { + 'loop_: while ((((i) < (5)) as i32) != 0) { goto_block!({ '__entry: { - if ((((i) == (2)) as i32) != 0) { + if ((((i) == (1)) as i32) != 0) { i.postfix_inc(); continue 'loop_; } - if ((((i) == (5)) as i32) != 0) { + if ((((i) == (4)) as i32) != 0) { break; } - if (((((i) % (2)) == (0)) as i32) != 0) { - goto!('even); - } - total += 1; + goto!('add); } - 'even: { - total += 10; + 'add: { + sum += 1; } }); i.postfix_inc(); } - return total; + return sum; } pub fn main() { unsafe { @@ -38,12 +35,6 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - assert!( - ((((unsafe { - let _n: i32 = 10; - loopctl_0(_n) - }) == (42)) as i32) - != 0) - ); + assert!(((((unsafe { loopctl_0() }) == (3)) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/goto_switch_exit.rs b/tests/unit/out/unsafe/goto_switch_exit.rs deleted file mode 100644 index fc1bbaeb..00000000 --- a/tests/unit/out/unsafe/goto_switch_exit.rs +++ /dev/null @@ -1,67 +0,0 @@ -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; - 'switch: { - let __match_cond = n; - match __match_cond { - v if v == 1 => { - ret = 10; - goto!('out); - } - v if v == 2 => { - ret = 20; - break 'switch; - } - _ => { - ret = 30; - break 'switch; - } - } - }; - ret += 1; - } - '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; - classify_0(_n) - }) == (10)) as i32) - != 0) - ); - assert!( - ((((unsafe { - let _n: i32 = 2; - classify_0(_n) - }) == (21)) as i32) - != 0) - ); - assert!( - ((((unsafe { - let _n: i32 = 9; - classify_0(_n) - }) == (31)) as i32) - != 0) - ); - return 0; -} From c0628560a5645f29fbd3d1663c3c5b02a85e9f05 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 1 Jun 2026 13:24:48 +0100 Subject: [PATCH 4/4] Avoid mutable Values in refcount --- cpp2rust/converter/converter.cpp | 23 +++++++++++-------- cpp2rust/converter/converter.h | 2 ++ .../converter/models/converter_refcount.cpp | 11 +++++++++ .../converter/models/converter_refcount.h | 2 ++ .../out/refcount/goto_aggregate_default.rs | 20 ++++++++-------- tests/unit/out/refcount/goto_backward.rs | 8 +++---- tests/unit/out/refcount/goto_cleanup.rs | 12 +++++----- tests/unit/out/refcount/goto_multi_label.rs | 4 ++-- tests/unit/out/refcount/goto_nested_label.rs | 4 ++-- .../out/refcount/goto_switch_fallthrough.rs | 4 ++-- 10 files changed, 54 insertions(+), 36 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 6f617631..c60e9527 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -474,11 +474,9 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { auto *method_or_null = curr_function_ ? clang::dyn_cast(curr_function_) : nullptr; - if (hoisted_decls_.contains(decl) && !qual_type->isReferenceType()) { - StrCat("mut"); - } else if (!qual_type.isConstQualified() && !qual_type->isReferenceType() && - ((method_or_null == nullptr) || !method_or_null->isVirtual()) && - !IsGlobalVar(decl)) { + if (!qual_type.isConstQualified() && !qual_type->isReferenceType() && + ((method_or_null == nullptr) || !method_or_null->isVirtual()) && + !IsGlobalVar(decl)) { StrCat(keyword_mut_); } @@ -521,13 +519,18 @@ 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)) { - if (decl->hasInit()) { - StrCat(GetNamedDeclAsString(decl), token::kAssign); - ConvertVarInit(decl->getType(), decl->getInit()); - StrCat(token::kSemiColon); - } + EmitHoistedInArmAssignment(decl); return; } if (!ConvertVarDeclSkipInit(decl)) { diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 1a629801..6292335c 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -92,6 +92,8 @@ class Converter : public clang::RecursiveASTVisitor { void ConvertVarDecl(clang::VarDecl *decl); + virtual void EmitHoistedInArmAssignment(clang::VarDecl *decl); + void ConvertVarDeclInitializer(clang::VarDecl *decl); virtual void ConvertGlobalVarDecl(clang::VarDecl *decl); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 929618b4..2717a270 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -628,6 +628,17 @@ bool ConverterRefCount::ConvertVarDeclSkipInit(clang::VarDecl *decl) { 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 131847e5..5332115c 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -59,6 +59,8 @@ class ConverterRefCount final : public Converter { 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/out/refcount/goto_aggregate_default.rs b/tests/unit/out/refcount/goto_aggregate_default.rs index 004f2355..d3cd131d 100644 --- a/tests/unit/out/refcount/goto_aggregate_default.rs +++ b/tests/unit/out/refcount/goto_aggregate_default.rs @@ -25,26 +25,26 @@ impl ByteRepr for Point { } pub fn agg_0(n: i32) -> i32 { let n: Value = Rc::new(RefCell::new(n)); - let mut buf40: Value> = Rc::new(RefCell::new( + let buf40: Value> = Rc::new(RefCell::new( (0..40).map(|_| ::default()).collect::>(), )); - let mut buf256: Value> = Rc::new(RefCell::new( + let buf256: Value> = Rc::new(RefCell::new( (0..256).map(|_| ::default()).collect::>(), )); - let mut arr64: Value> = Rc::new(RefCell::new( + let arr64: Value> = Rc::new(RefCell::new( (0..64).map(|_| ::default()).collect::>(), )); - let mut longs: Value> = Rc::new(RefCell::new( + let longs: Value> = Rc::new(RefCell::new( (0..33).map(|_| ::default()).collect::>(), )); - let mut p: Value = >::default(); - let mut ptr: Value> = Rc::new(RefCell::new(Ptr::::null())); - let mut fp: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); - let mut file: Value> = Rc::new(RefCell::new(Ptr::null())); - let mut total: Value = >::default(); + 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 = Rc::new(RefCell::new(0)); + *total.borrow_mut() = 0; if ((((*n.borrow()) < 0) as i32) != 0) { goto!('out); } diff --git a/tests/unit/out/refcount/goto_backward.rs b/tests/unit/out/refcount/goto_backward.rs index c67f0836..fbfe8a33 100644 --- a/tests/unit/out/refcount/goto_backward.rs +++ b/tests/unit/out/refcount/goto_backward.rs @@ -8,12 +8,12 @@ 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 mut count: Value = >::default(); - let mut acc: Value = >::default(); + let count: Value = >::default(); + let acc: Value = >::default(); goto_block!({ '__entry: { - count = Rc::new(RefCell::new(0)); - acc = Rc::new(RefCell::new(0)); + *count.borrow_mut() = 0; + *acc.borrow_mut() = 0; } 'again: { (*count.borrow_mut()) += 1; diff --git a/tests/unit/out/refcount/goto_cleanup.rs b/tests/unit/out/refcount/goto_cleanup.rs index 86928b1e..a44fb20f 100644 --- a/tests/unit/out/refcount/goto_cleanup.rs +++ b/tests/unit/out/refcount/goto_cleanup.rs @@ -8,10 +8,10 @@ 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 mut ret: Value = >::default(); + let ret: Value = >::default(); goto_block!({ '__entry: { - ret = Rc::new(RefCell::new(0)); + *ret.borrow_mut() = 0; if ((((*n.borrow()) < 0) as i32) != 0) { (*ret.borrow_mut()) = -1_i32; goto!('out); @@ -26,10 +26,10 @@ pub fn early_0(n: i32) -> i32 { } pub fn from_loop_1(n: i32) -> i32 { let n: Value = Rc::new(RefCell::new(n)); - let mut ret: Value = >::default(); + let ret: Value = >::default(); goto_block!({ '__entry: { - ret = Rc::new(RefCell::new(0)); + *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) { @@ -49,10 +49,10 @@ pub fn from_loop_1(n: i32) -> i32 { } pub fn from_switch_2(n: i32) -> i32 { let n: Value = Rc::new(RefCell::new(n)); - let mut ret: Value = >::default(); + let ret: Value = >::default(); goto_block!({ '__entry: { - ret = Rc::new(RefCell::new(0)); + *ret.borrow_mut() = 0; 'switch: { let __match_cond = (*n.borrow()); match __match_cond { diff --git a/tests/unit/out/refcount/goto_multi_label.rs b/tests/unit/out/refcount/goto_multi_label.rs index 721e7940..4a3c74bf 100644 --- a/tests/unit/out/refcount/goto_multi_label.rs +++ b/tests/unit/out/refcount/goto_multi_label.rs @@ -8,10 +8,10 @@ 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 mut ret: Value = >::default(); + let ret: Value = >::default(); goto_block!({ '__entry: { - ret = Rc::new(RefCell::new(0)); + *ret.borrow_mut() = 0; if ((((*n.borrow()) < 0) as i32) != 0) { goto!('error); } diff --git a/tests/unit/out/refcount/goto_nested_label.rs b/tests/unit/out/refcount/goto_nested_label.rs index 7a9fb7f9..8dec8dd8 100644 --- a/tests/unit/out/refcount/goto_nested_label.rs +++ b/tests/unit/out/refcount/goto_nested_label.rs @@ -11,10 +11,10 @@ pub fn scan_0(n: i32) -> i32 { 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 mut j: Value = >::default(); + let j: Value = >::default(); goto_block!({ '__entry: { - j = Rc::new(RefCell::new(0)); + *j.borrow_mut() = 0; 'loop_: while ((((*j.borrow()) < 10) as i32) != 0) { if ((((*j.borrow()) == 5) as i32) != 0) { goto!('next); diff --git a/tests/unit/out/refcount/goto_switch_fallthrough.rs b/tests/unit/out/refcount/goto_switch_fallthrough.rs index 469c67ce..74581ceb 100644 --- a/tests/unit/out/refcount/goto_switch_fallthrough.rs +++ b/tests/unit/out/refcount/goto_switch_fallthrough.rs @@ -8,10 +8,10 @@ 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 mut ret: Value = >::default(); + let ret: Value = >::default(); goto_block!({ '__entry: { - ret = Rc::new(RefCell::new(0)); + *ret.borrow_mut() = 0; switch!(match (*n.borrow()) { v if v == 0 => { (*ret.borrow_mut()) += 1;