From 30e51adb690973b82809d1f68817fc332ee5142f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 7 Jul 2026 15:47:25 +0100 Subject: [PATCH 1/2] Fix hoisted initialization outside goto_block --- .../converter/models/converter_refcount.cpp | 2 +- tests/unit/goto_cleanup.c | 20 +++++++ tests/unit/out/refcount/goto_cleanup.rs | 52 +++++++++++++++++++ tests/unit/out/unsafe/goto_cleanup.rs | 30 +++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index aa0a46aa..92159f7a 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -726,7 +726,7 @@ void ConverterRefCount::EmitHoistedInArmAssignment(clang::VarDecl *decl) { PushConversionKind push(*this, ConversionKind::Unboxed); StrCat(token::kStar, GetNamedDeclAsString(decl), ".borrow_mut()", token::kAssign); - Convert(decl->getInit()); + StrCat(ConvertFreshRValue(decl->getInit(), decl->getType())); StrCat(token::kSemiColon); } diff --git a/tests/unit/goto_cleanup.c b/tests/unit/goto_cleanup.c index ca974f8b..b8a1faba 100644 --- a/tests/unit/goto_cleanup.c +++ b/tests/unit/goto_cleanup.c @@ -41,6 +41,22 @@ static int from_switch(int n) { return ret; } +struct wrapper { + int *item; +}; + +static int via_pointer(struct wrapper *w, int fail) { + int ret = 0; + int *item = w->item; + if (fail) { + ret = -1; + goto out; + } + ret = *item; +out: + return ret; +} + int main(void) { assert(early(-1) == -1); assert(early(5) == 100); @@ -48,5 +64,9 @@ int main(void) { assert(from_loop(10) == 7); assert(from_switch(1) == 10); assert(from_switch(2) == 999); + int value = 42; + struct wrapper w = {&value}; + assert(via_pointer(&w, 0) == 42); + assert(via_pointer(&w, 1) == -1); return 0; } diff --git a/tests/unit/out/refcount/goto_cleanup.rs b/tests/unit/out/refcount/goto_cleanup.rs index f2092367..6c2eb077 100644 --- a/tests/unit/out/refcount/goto_cleanup.rs +++ b/tests/unit/out/refcount/goto_cleanup.rs @@ -77,6 +77,52 @@ pub fn from_switch_2(n: i32) -> i32 { }); panic!("ub: non-void function does not return a value") } +#[derive(Default)] +pub struct wrapper { + pub item: Value>, +} +impl Clone for wrapper { + fn clone(&self) -> Self { + Self { + item: Rc::new(RefCell::new((*self.item.borrow()).clone())), + } + } +} +impl ByteRepr for wrapper { + fn byte_size() -> usize { + 8 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.item.borrow()).to_bytes(&mut buf[0..8]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + item: Rc::new(RefCell::new(>::from_bytes(&buf[0..8]))), + } + } +} +pub fn via_pointer_3(w: Ptr, fail: i32) -> i32 { + let w: Value> = Rc::new(RefCell::new(w)); + let fail: Value = Rc::new(RefCell::new(fail)); + let ret: Value = >::default(); + let item: Value> = Rc::new(RefCell::new(Ptr::::null())); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + *item.borrow_mut() = (*(*(*w.borrow()).upgrade().deref()).item.borrow()).clone(); + if ((*fail.borrow()) != 0) { + (*ret.borrow_mut()) = -1_i32; + goto!('out); + } + let __rhs = ((*item.borrow()).read()); + (*ret.borrow_mut()) = __rhs; + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} pub fn main() { std::process::exit(main_0()); } @@ -87,5 +133,11 @@ fn main_0() -> i32 { assert!((((({ from_loop_1(10,) }) == 7) as i32) != 0)); assert!((((({ from_switch_2(1,) }) == 10) as i32) != 0)); assert!((((({ from_switch_2(2,) }) == 999) as i32) != 0)); + let value: Value = Rc::new(RefCell::new(42)); + let w: Value = Rc::new(RefCell::new(wrapper { + item: Rc::new(RefCell::new((value.as_pointer()))), + })); + assert!((((({ via_pointer_3((w.as_pointer()), 0,) }) == 42) as i32) != 0)); + assert!((((({ via_pointer_3((w.as_pointer()), 1,) }) == -1_i32) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/goto_cleanup.rs b/tests/unit/out/unsafe/goto_cleanup.rs index e3e55df3..db2f6fd3 100644 --- a/tests/unit/out/unsafe/goto_cleanup.rs +++ b/tests/unit/out/unsafe/goto_cleanup.rs @@ -73,6 +73,30 @@ pub unsafe fn from_switch_2(mut n: i32) -> i32 { }); panic!("ub: non-void function does not return a value") } +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct wrapper { + pub item: *mut i32, +} +pub unsafe fn via_pointer_3(mut w: *mut wrapper, mut fail: i32) -> i32 { + let mut ret: i32 = 0_i32; + let mut item: *mut i32 = std::ptr::null_mut(); + goto_block!({ + '__entry: { + ret = 0; + item = (*w).item; + if (fail != 0) { + ret = -1_i32; + goto!('out); + } + ret = (*item); + } + 'out: { + return ret; + } + }); + panic!("ub: non-void function does not return a value") +} pub fn main() { unsafe { std::process::exit(main_0() as i32); @@ -85,5 +109,11 @@ unsafe fn main_0() -> i32 { assert!(((((unsafe { from_loop_1(10,) }) == (7)) as i32) != 0)); assert!(((((unsafe { from_switch_2(1,) }) == (10)) as i32) != 0)); assert!(((((unsafe { from_switch_2(2,) }) == (999)) as i32) != 0)); + let mut value: i32 = 42; + let mut w: wrapper = wrapper { + item: (&mut value as *mut i32), + }; + assert!(((((unsafe { via_pointer_3((&mut w as *mut wrapper), 0,) }) == (42)) as i32) != 0)); + assert!(((((unsafe { via_pointer_3((&mut w as *mut wrapper), 1,) }) == (-1_i32)) as i32) != 0)); return 0; } From 4377d71e2d0c156972ace9fbe68c48b2e64b2060 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 7 Jul 2026 18:18:30 +0100 Subject: [PATCH 2/2] Refactor EmitHoistedInArmAssignment to use the same logic as ConvertVarInit --- .../converter/models/converter_refcount.cpp | 47 +++++++++---------- .../converter/models/converter_refcount.h | 2 + tests/unit/goto_cleanup.c | 16 +++++++ tests/unit/out/refcount/goto_cleanup.rs | 33 +++++++++++++ tests/unit/out/unsafe/goto_cleanup.rs | 26 ++++++++++ 5 files changed, 100 insertions(+), 24 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 92159f7a..f6b6b7d0 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -723,10 +723,10 @@ void ConverterRefCount::EmitHoistedInArmAssignment(clang::VarDecl *decl) { if (!decl->hasInit()) { return; } - PushConversionKind push(*this, ConversionKind::Unboxed); + PushConversionKind push(*this, ConversionKind::FullRefCount); StrCat(token::kStar, GetNamedDeclAsString(decl), ".borrow_mut()", token::kAssign); - StrCat(ConvertFreshRValue(decl->getInit(), decl->getType())); + StrCat(ConvertVarInitValue(decl->getType(), decl->getInit())); StrCat(token::kSemiColon); } @@ -1943,35 +1943,34 @@ ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) { return attrs; } -void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, - clang::Expr *expr) { +std::string ConverterRefCount::ConvertVarInitValue(clang::QualType qual_type, + clang::Expr *expr) { if (auto lambda = clang::dyn_cast( expr->IgnoreUnlessSpelledInSource())) { - std::string str; - { - Buffer buf(*this); - PushConversionKind push(*this, ConversionKind::Unboxed); - if (qual_type->isFunctionPointerType() && lambda->capture_size() == 0) { - StrCat("FnPtr::new("); - VisitLambdaExpr(lambda); - StrCat(')'); - } else { - VisitLambdaExpr(lambda); - } - str = std::move(buf).str(); + Buffer buf(*this); + PushConversionKind push(*this, ConversionKind::Unboxed); + if (qual_type->isFunctionPointerType() && lambda->capture_size() == 0) { + StrCat("FnPtr::new("); + VisitLambdaExpr(lambda); + StrCat(')'); + } else { + VisitLambdaExpr(lambda); } - StrCat(BoxValue(std::move(str))); - return; + return std::move(buf).str(); } - bool is_ref = qual_type->isReferenceType(); - PushConversionKind push(*this, ConversionKind::Unboxed, is_ref); PushInitType init_type(*this, qual_type); - if (is_ref || qual_type->isFunctionPointerType()) { - StrCat(BoxValue(ConvertFreshPointer(expr))); - } else { - StrCat(BoxValue(ConvertFreshRValue(expr, qual_type))); + if (qual_type->isReferenceType() || qual_type->isFunctionPointerType()) { + return ConvertFreshPointer(expr); } + return ConvertFreshRValue(expr, qual_type); +} + +void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, + clang::Expr *expr) { + bool is_ref = qual_type->isReferenceType(); + PushConversionKind push(*this, ConversionKind::Unboxed, is_ref); + StrCat(BoxValue(ConvertVarInitValue(qual_type, expr))); } void ConverterRefCount::EmitSetOrAssign(clang::Expr *lhs, diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index e58853e1..39b12671 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -167,6 +167,8 @@ class ConverterRefCount final : public Converter { void ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) override; + std::string ConvertVarInitValue(clang::QualType qual_type, clang::Expr *expr); + void ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs, std::string_view assign_operator) override; diff --git a/tests/unit/goto_cleanup.c b/tests/unit/goto_cleanup.c index b8a1faba..f2d817c0 100644 --- a/tests/unit/goto_cleanup.c +++ b/tests/unit/goto_cleanup.c @@ -57,6 +57,20 @@ static int via_pointer(struct wrapper *w, int fail) { return ret; } +static int via_arrays(int fail) { + int ret = 0; + unsigned char remain[4] = {0}; + char name[5] = "wxyz"; + if (fail) { + ret = -1; + goto out; + } + remain[1] = 9; + ret = remain[0] + remain[1] + (name[0] == 'w') + (name[4] == '\0'); +out: + return ret; +} + int main(void) { assert(early(-1) == -1); assert(early(5) == 100); @@ -68,5 +82,7 @@ int main(void) { struct wrapper w = {&value}; assert(via_pointer(&w, 0) == 42); assert(via_pointer(&w, 1) == -1); + assert(via_arrays(0) == 11); + assert(via_arrays(1) == -1); return 0; } diff --git a/tests/unit/out/refcount/goto_cleanup.rs b/tests/unit/out/refcount/goto_cleanup.rs index 6c2eb077..baa003ec 100644 --- a/tests/unit/out/refcount/goto_cleanup.rs +++ b/tests/unit/out/refcount/goto_cleanup.rs @@ -123,6 +123,37 @@ pub fn via_pointer_3(w: Ptr, fail: i32) -> i32 { }); panic!("ub: non-void function does not return a value") } +pub fn via_arrays_4(fail: i32) -> i32 { + let fail: Value = Rc::new(RefCell::new(fail)); + let ret: Value = >::default(); + let remain: Value> = Rc::new(RefCell::new( + (0..4).map(|_| ::default()).collect::>(), + )); + let name: Value> = Rc::new(RefCell::new( + (0..5).map(|_| ::default()).collect::>(), + )); + goto_block!({ + '__entry: { + *ret.borrow_mut() = 0; + *remain.borrow_mut() = + Box::new([0_u8, ::default(), ::default(), ::default()]); + *name.borrow_mut() = Box::from(*b"wxyz\0"); + if ((*fail.borrow()) != 0) { + (*ret.borrow_mut()) = -1_i32; + goto!('out); + } + (*remain.borrow_mut())[(1) as usize] = 9_u8; + (*ret.borrow_mut()) = (((((*remain.borrow())[(0) as usize] as i32) + + ((*remain.borrow())[(1) as usize] as i32)) + + ((((*name.borrow())[(0) as usize] as i32) == ('w' as i32)) as i32)) + + ((((*name.borrow())[(4) as usize] as i32) == ('\0' as i32)) as i32)); + } + 'out: { + return (*ret.borrow()); + } + }); + panic!("ub: non-void function does not return a value") +} pub fn main() { std::process::exit(main_0()); } @@ -139,5 +170,7 @@ fn main_0() -> i32 { })); assert!((((({ via_pointer_3((w.as_pointer()), 0,) }) == 42) as i32) != 0)); assert!((((({ via_pointer_3((w.as_pointer()), 1,) }) == -1_i32) as i32) != 0)); + assert!((((({ via_arrays_4(0,) }) == 11) as i32) != 0)); + assert!((((({ via_arrays_4(1,) }) == -1_i32) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/goto_cleanup.rs b/tests/unit/out/unsafe/goto_cleanup.rs index db2f6fd3..b5e328f4 100644 --- a/tests/unit/out/unsafe/goto_cleanup.rs +++ b/tests/unit/out/unsafe/goto_cleanup.rs @@ -97,6 +97,30 @@ pub unsafe fn via_pointer_3(mut w: *mut wrapper, mut fail: i32) -> i32 { }); panic!("ub: non-void function does not return a value") } +pub unsafe fn via_arrays_4(mut fail: i32) -> i32 { + let mut ret: i32 = 0_i32; + let mut remain: [u8; 4] = [0_u8; 4]; + let mut name: [libc::c_char; 5] = [(0 as libc::c_char); 5]; + goto_block!({ + '__entry: { + ret = 0; + remain = [0_u8, 0_u8, 0_u8, 0_u8]; + name = std::mem::transmute(*b"wxyz\0"); + if (fail != 0) { + ret = -1_i32; + goto!('out); + } + remain[(1) as usize] = 9_u8; + ret = ((((remain[(0) as usize] as i32) + (remain[(1) as usize] as i32)) + + (((name[(0) as usize] as i32) == ('w' as i32)) as i32)) + + (((name[(4) as usize] as i32) == ('\0' as i32)) as 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); @@ -115,5 +139,7 @@ unsafe fn main_0() -> i32 { }; assert!(((((unsafe { via_pointer_3((&mut w as *mut wrapper), 0,) }) == (42)) as i32) != 0)); assert!(((((unsafe { via_pointer_3((&mut w as *mut wrapper), 1,) }) == (-1_i32)) as i32) != 0)); + assert!(((((unsafe { via_arrays_4(0,) }) == (11)) as i32) != 0)); + assert!(((((unsafe { via_arrays_4(1,) }) == (-1_i32)) as i32) != 0)); return 0; }