From 2f5e2a93615b291882753a13c3b7b4f7ecba3da9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 23 Jun 2026 13:19:04 +0100 Subject: [PATCH 01/14] Add UnionStore --- libcc2rs/src/lib.rs | 3 +++ libcc2rs/src/rc.rs | 9 +++++++++ libcc2rs/src/union.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 libcc2rs/src/union.rs diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index 4288464f..88c73e74 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -7,6 +7,9 @@ pub use reinterpret::ByteRepr; mod rc; pub use rc::*; +mod union; +pub use union::*; + mod fn_ptr; pub use fn_ptr::FnPtr; diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index ccf2f991..71f0c993 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1246,6 +1246,15 @@ impl AsPointerDyn for Rc> { impl ByteRepr for Ptr {} +impl Ptr { + pub(crate) fn reinterpreted(alloc: Rc, byte_offset: usize) -> Self { + Ptr { + offset: byte_offset, + kind: PtrKind::Reinterpreted(alloc), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/libcc2rs/src/union.rs b/libcc2rs/src/union.rs new file mode 100644 index 00000000..92204387 --- /dev/null +++ b/libcc2rs/src/union.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use std::{cell::RefCell, rc::Rc}; + +use crate::Ptr; +use crate::reinterpret::{ByteRepr, OriginalAlloc, SliceOriginalAlloc}; + +pub struct UnionStore { + bytes: Rc>>, +} + +impl UnionStore { + pub fn new(size: usize) -> Self { + UnionStore { + bytes: Rc::new(RefCell::new(vec![0u8; size])), + } + } + + pub fn pod(&self, offset: usize) -> Ptr { + let alloc: Rc = Rc::new(SliceOriginalAlloc { + weak: Rc::downgrade(&self.bytes), + }); + Ptr::reinterpreted(alloc, offset) + } +} + +impl Clone for UnionStore { + fn clone(&self) -> Self { + UnionStore { + bytes: Rc::new(RefCell::new(self.bytes.borrow().clone())), + } + } +} From 77b0ea8f0f13ef862eef1c1b58cd10f6e7c0fa7e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 23 Jun 2026 13:17:23 +0100 Subject: [PATCH 02/14] Codegen for safe union --- cpp2rust/converter/converter.cpp | 33 ++++++++++++++- cpp2rust/converter/converter.h | 2 + .../converter/models/converter_refcount.cpp | 41 +++++++++++++++++++ .../converter/models/converter_refcount.h | 2 + 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 09e9d526..2df856bc 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -753,6 +753,11 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { } } + if (decl->isUnion()) { + EmitRustUnion(decl); + return; + } + // Derived traits if (EmitsReprCForRecords()) { StrCat("#[repr(C)]"); @@ -770,8 +775,7 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { auto access = clang::dyn_cast(decl) ? AccessSpecifierAsString(decl->getAccess()) : keyword::kPub; - StrCat(access, decl->isUnion() ? keyword::kUnion : keyword::kStruct, - GetRecordName(decl)); + StrCat(access, keyword::kStruct, GetRecordName(decl)); { PushBrace brace(*this); for (auto *field : decl->fields()) { @@ -817,6 +821,31 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { AddByteReprTrait(decl); } +void Converter::EmitRustUnion(clang::RecordDecl *decl) { + if (EmitsReprCForRecords()) { + StrCat("#[repr(C)]"); + } + auto attrs = GetStructAttributes(decl); + Mapper::SetDerives(ctx_.getCanonicalTagType(decl), + std::vector(attrs.begin(), attrs.end())); + StrCat("#[derive("); + for (auto *attr : attrs) { + StrCat(attr, ','); + } + StrCat(")]"); + + StrCat(keyword::kPub, keyword::kUnion, GetRecordName(decl)); + { + PushBrace brace(*this); + for (auto *field : decl->fields()) { + VisitFieldDecl(field); + } + } + + AddDefaultTrait(decl); + AddByteReprTrait(decl); +} + bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) { if (clang::isa(decl)) { materializeTemplateSpecialization(decl); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 735b118e..fbdd4e59 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -112,6 +112,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual void EmitRustStructOrUnion(clang::RecordDecl *decl); + virtual void EmitRustUnion(clang::RecordDecl *decl); + virtual bool EmitsReprCForRecords() const { return true; } virtual bool VisitCXXMethodDecl(clang::CXXMethodDecl *decl); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 17b4f149..71f4f74e 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -469,6 +469,42 @@ void ConverterRefCount::AddDefaultTrait(const clang::RecordDecl *decl) { } void ConverterRefCount::AddDefaultTraitForUnion(const clang::RecordDecl *decl) { + auto name = GetRecordName(decl); + StrCat(std::format("impl Default for {}", name)); + PushBrace impl_brace(*this); + StrCat("fn default() -> Self"); + PushBrace fn_brace(*this); + StrCat(std::format("{} {{ __store: libcc2rs::UnionStore::new({}) }}", name, + ctx_.getASTRecordLayout(decl).getSize().getQuantity())); +} + +void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { + auto name = GetRecordName(decl); + + auto attrs = GetStructAttributes(decl); + Mapper::SetDerives(ctx_.getCanonicalTagType(decl), + std::vector(attrs.begin(), attrs.end())); + StrCat("#[derive("); + for (auto *attr : attrs) { + StrCat(attr, ','); + } + StrCat(")]"); + + StrCat( + std::format("pub struct {} {{ __store: libcc2rs::UnionStore, }}", name)); + + StrCat(std::format("impl {}", name)); + { + PushBrace impl_brace(*this); + for (auto *field : decl->fields()) { + StrCat(std::format( + "pub fn {}(&self) -> Ptr<{}> {{ self.__store.pod(0) }}", + GetNamedDeclAsString(field), Mapper::Map(field->getType()))); + } + } + + AddDefaultTrait(decl); + AddByteReprTrait(decl); } void ConverterRefCount::AddDropTrait(const clang::CXXRecordDecl *decl) { @@ -1798,6 +1834,11 @@ std::vector ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) { std::vector attrs; + if (decl->isUnion()) { + attrs.emplace_back("Clone"); + return attrs; + } + if (RecordDerivesDefault(decl)) { attrs.emplace_back("Default"); } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 122a930d..b35d71b3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -28,6 +28,8 @@ class ConverterRefCount final : public Converter { bool VisitCXXRecordDecl(clang::CXXRecordDecl *decl) override; + void EmitRustUnion(clang::RecordDecl *decl) override; + bool EmitsReprCForRecords() const override { return false; } void ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl, From f6573456989dff5d5394800a617092679f008b24 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 23 Jun 2026 13:22:44 +0100 Subject: [PATCH 03/14] pod -> reinterpret and UnionStore -> UnionStorage --- cpp2rust/converter/models/converter_refcount.cpp | 6 +++--- libcc2rs/src/union.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 71f4f74e..213489dd 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -474,7 +474,7 @@ void ConverterRefCount::AddDefaultTraitForUnion(const clang::RecordDecl *decl) { PushBrace impl_brace(*this); StrCat("fn default() -> Self"); PushBrace fn_brace(*this); - StrCat(std::format("{} {{ __store: libcc2rs::UnionStore::new({}) }}", name, + StrCat(std::format("{} {{ __store: libcc2rs::UnionStorage::new({}) }}", name, ctx_.getASTRecordLayout(decl).getSize().getQuantity())); } @@ -491,14 +491,14 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { StrCat(")]"); StrCat( - std::format("pub struct {} {{ __store: libcc2rs::UnionStore, }}", name)); + std::format("pub struct {} {{ __store: libcc2rs::UnionStorage, }}", name)); StrCat(std::format("impl {}", name)); { PushBrace impl_brace(*this); for (auto *field : decl->fields()) { StrCat(std::format( - "pub fn {}(&self) -> Ptr<{}> {{ self.__store.pod(0) }}", + "pub fn {}(&self) -> Ptr<{}> {{ self.__store.reinterpret(0) }}", GetNamedDeclAsString(field), Mapper::Map(field->getType()))); } } diff --git a/libcc2rs/src/union.rs b/libcc2rs/src/union.rs index 92204387..2501ebbf 100644 --- a/libcc2rs/src/union.rs +++ b/libcc2rs/src/union.rs @@ -6,18 +6,18 @@ use std::{cell::RefCell, rc::Rc}; use crate::Ptr; use crate::reinterpret::{ByteRepr, OriginalAlloc, SliceOriginalAlloc}; -pub struct UnionStore { +pub struct UnionStorage { bytes: Rc>>, } -impl UnionStore { +impl UnionStorage { pub fn new(size: usize) -> Self { - UnionStore { + UnionStorage { bytes: Rc::new(RefCell::new(vec![0u8; size])), } } - pub fn pod(&self, offset: usize) -> Ptr { + pub fn reinterpret(&self, offset: usize) -> Ptr { let alloc: Rc = Rc::new(SliceOriginalAlloc { weak: Rc::downgrade(&self.bytes), }); @@ -25,9 +25,9 @@ impl UnionStore { } } -impl Clone for UnionStore { +impl Clone for UnionStorage { fn clone(&self) -> Self { - UnionStore { + UnionStorage { bytes: Rc::new(RefCell::new(self.bytes.borrow().clone())), } } From ed077f238fce1c9c0b32fe4c2c3aa6228f0e85a6 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 23 Jun 2026 13:23:06 +0100 Subject: [PATCH 04/14] Codegen for union accessors --- .../converter/models/converter_refcount.cpp | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 213489dd..db56f824 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -490,8 +490,8 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { } StrCat(")]"); - StrCat( - std::format("pub struct {} {{ __store: libcc2rs::UnionStorage, }}", name)); + StrCat(std::format("pub struct {} {{ __store: libcc2rs::UnionStorage, }}", + name)); StrCat(std::format("impl {}", name)); { @@ -1496,6 +1496,32 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) { return false; } + if (auto *parent = + clang::dyn_cast(member->getDeclContext()); + parent && parent->isUnion() && clang::isa(member)) { + std::string str; + { + Buffer buf(*this); + PushExprKind push(*this, + isLValue() ? ExprKind::LValue : ExprKind::RValue); + Converter::ConvertMemberExpr(expr); // e.g. (*u.borrow()).i + str = std::move(buf).str(); + } + str += "()"; + + if (isAddrOf()) { + StrCat(str); + computed_expr_type_ = ComputedExprType::Pointer; + return false; + } + if (isLValue()) { + pending_deref_.set(str); + return false; + } + StrCat(DerefPtrExpr(str, member->getType())); + return false; + } + std::string str; if (known) { str = GetMappedAsString(expr); From 3b01e9b5875a1bfc498f187715ee1d41e12e0194 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 23 Jun 2026 13:25:52 +0100 Subject: [PATCH 05/14] Update tests --- .../converter/models/converter_refcount.cpp | 2 +- .../refcount/tag_vs_identifier_collision.rs | 187 +++++++++++++ tests/unit/out/refcount/union_basic.rs | 39 +++ .../union_pointer_pun_writethrough.rs | 39 +++ .../out/refcount/union_tagged_struct_arms.rs | 251 ++++++++++++++++++ tests/unit/tag_vs_identifier_collision.c | 1 - tests/unit/union_basic.c | 1 - tests/unit/union_pointer_pun_writethrough.c | 2 +- tests/unit/union_tagged_struct_arms.c | 2 +- 9 files changed, 519 insertions(+), 5 deletions(-) create mode 100644 tests/unit/out/refcount/tag_vs_identifier_collision.rs create mode 100644 tests/unit/out/refcount/union_basic.rs create mode 100644 tests/unit/out/refcount/union_pointer_pun_writethrough.rs create mode 100644 tests/unit/out/refcount/union_tagged_struct_arms.rs diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index db56f824..cac005c1 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1504,7 +1504,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) { Buffer buf(*this); PushExprKind push(*this, isLValue() ? ExprKind::LValue : ExprKind::RValue); - Converter::ConvertMemberExpr(expr); // e.g. (*u.borrow()).i + Converter::ConvertMemberExpr(expr); str = std::move(buf).str(); } str += "()"; diff --git a/tests/unit/out/refcount/tag_vs_identifier_collision.rs b/tests/unit/out/refcount/tag_vs_identifier_collision.rs new file mode 100644 index 00000000..0623f19f --- /dev/null +++ b/tests/unit/out/refcount/tag_vs_identifier_collision.rs @@ -0,0 +1,187 @@ +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(Clone, Copy, PartialEq, Debug, Default)] +enum widget_enum { + #[default] + MODE_IDLE = 0, + MODE_ACTIVE = 1, + MODE_DONE = 2, +} +impl From for widget_enum { + fn from(n: i32) -> widget_enum { + match n { + 0 => widget_enum::MODE_IDLE, + 1 => widget_enum::MODE_ACTIVE, + 2 => widget_enum::MODE_DONE, + _ => panic!("invalid widget_enum value: {}", n), + } + } +} +libcc2rs::impl_enum_inc_dec!(widget_enum); +#[derive(Default)] +pub struct widget { + pub id: Value, + pub mode: Value, +} +impl ByteRepr for widget {} +#[derive(Default)] +pub struct point_struct { + pub x: Value, + pub y: Value, +} +impl ByteRepr for point_struct { + 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]))), + } + } +} +#[derive(Clone)] +pub struct point { + __store: libcc2rs::UnionStorage, +} +impl point { + pub fn whole(&self) -> Ptr { + self.__store.reinterpret(0) + } + pub fn half(&self) -> Ptr { + self.__store.reinterpret(0) + } +} +impl Default for point { + fn default() -> Self { + point { + __store: libcc2rs::UnionStorage::new(4), + } + } +} +impl ByteRepr for point {} +#[derive(Clone)] +pub struct slot_union { + __store: libcc2rs::UnionStorage, +} +impl slot_union { + pub fn i(&self) -> Ptr { + self.__store.reinterpret(0) + } + pub fn u(&self) -> Ptr { + self.__store.reinterpret(0) + } +} +impl Default for slot_union { + fn default() -> Self { + slot_union { + __store: libcc2rs::UnionStorage::new(4), + } + } +} +impl ByteRepr for slot_union {} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum slot { + #[default] + SLOT_A = 0, + SLOT_B = 1, +} +impl From for slot { + fn from(n: i32) -> slot { + match n { + 0 => slot::SLOT_A, + 1 => slot::SLOT_B, + _ => panic!("invalid slot value: {}", n), + } + } +} +libcc2rs::impl_enum_inc_dec!(slot); +#[derive(Default)] +pub struct Inner { + pub tag_field: Value, +} +impl ByteRepr for Inner { + fn to_bytes(&self, buf: &mut [u8]) { + (*self.tag_field.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + tag_field: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +#[derive(Default)] +pub struct Outer { + pub field: Value, +} +impl ByteRepr for Outer {} +#[derive(Default)] +pub struct Inner_struct { + pub typedef_field: Value, +} +impl ByteRepr for Inner_struct { + fn to_bytes(&self, buf: &mut [u8]) { + (*self.typedef_field.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + typedef_field: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +pub fn is_active_0(w: Ptr) -> i32 { + let w: Value> = Rc::new(RefCell::new(w)); + return (({ + let _lhs = ((*(*(*w.borrow()).upgrade().deref()).mode.borrow()) as u32).clone(); + _lhs == ((widget_enum::MODE_ACTIVE as i32) as u32) + }) as i32); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let w: Value = >::default(); + (*(*w.borrow()).id.borrow_mut()) = 7; + (*(*w.borrow()).mode.borrow_mut()) = widget_enum::MODE_ACTIVE; + assert!( + (({ + let _w: Ptr = (w.as_pointer()); + is_active_0(_w) + }) != 0) + ); + (*(*w.borrow()).mode.borrow_mut()) = widget_enum::MODE_DONE; + assert!( + (((((*(*w.borrow()).mode.borrow()) as u32) == ((widget_enum::MODE_DONE as i32) as u32)) + as i32) + != 0) + ); + let p: Value = >::default(); + (*(*p.borrow()).x.borrow_mut()) = 3; + (*(*p.borrow()).y.borrow_mut()) = 4; + assert!((((((*(*p.borrow()).x.borrow()) + (*(*p.borrow()).y.borrow())) == 7) as i32) != 0)); + let up: Value = >::default(); + (*up.borrow_mut()).whole().write(5); + assert!((((((*up.borrow()).whole().read()) == 5) as i32) != 0)); + let b: Value = >::default(); + (*b.borrow_mut()).i().write(9); + assert!((((((*b.borrow()).i().read()) == 9) as i32) != 0)); + let e: Value = Rc::new(RefCell::new(slot::SLOT_B)); + assert!((((((*e.borrow()) as u32) == ((slot::SLOT_B as i32) as u32)) as i32) != 0)); + let inner_tag: Value = >::default(); + (*(*inner_tag.borrow()).tag_field.borrow_mut()) = 11; + assert!(((((*(*inner_tag.borrow()).tag_field.borrow()) == 11) as i32) != 0)); + let inner_typedef: Value = >::default(); + (*(*inner_typedef.borrow()).typedef_field.borrow_mut()) = 22; + assert!(((((*(*inner_typedef.borrow()).typedef_field.borrow()) == 22) as i32) != 0)); + let o: Value = >::default(); + (*(*(*o.borrow()).field.borrow()).tag_field.borrow_mut()) = 33; + assert!(((((*(*(*o.borrow()).field.borrow()).tag_field.borrow()) == 33) as i32) != 0)); + return (*(*w.borrow()).id.borrow()); +} diff --git a/tests/unit/out/refcount/union_basic.rs b/tests/unit/out/refcount/union_basic.rs new file mode 100644 index 00000000..f6fe7b26 --- /dev/null +++ b/tests/unit/out/refcount/union_basic.rs @@ -0,0 +1,39 @@ +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(Clone)] +pub struct basic { + __store: libcc2rs::UnionStorage, +} +impl basic { + pub fn i(&self) -> Ptr { + self.__store.reinterpret(0) + } + pub fn f(&self) -> Ptr { + self.__store.reinterpret(0) + } +} +impl Default for basic { + fn default() -> Self { + basic { + __store: libcc2rs::UnionStorage::new(4), + } + } +} +impl ByteRepr for basic {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let u: Value = >::default(); + (*u.borrow_mut()).i().write(42); + assert!((((((*u.borrow()).i().read()) == 42) as i32) != 0)); + (*u.borrow_mut()).f().write(3.140000105E+0); + assert!((((((*u.borrow()).f().read()) == 3.140000105E+0) as i32) != 0)); + return 0; +} diff --git a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs new file mode 100644 index 00000000..6ff3165c --- /dev/null +++ b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs @@ -0,0 +1,39 @@ +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 main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let x: Value = Rc::new(RefCell::new((-1_i32 as i64))); + #[derive(Clone)] + pub struct anon_0 { + __store: libcc2rs::UnionStorage, + } + impl anon_0 { + pub fn as_unsigned(&self) -> Ptr> { + self.__store.reinterpret(0) + } + pub fn as_signed(&self) -> Ptr> { + self.__store.reinterpret(0) + } + } + impl Default for anon_0 { + fn default() -> Self { + anon_0 { + __store: libcc2rs::UnionStorage::new(8), + } + } + } + impl ByteRepr for anon_0 {}; + let pp: Value = >::default(); + (*pp.borrow_mut()).as_signed().write((x.as_pointer())); + ((*pp.borrow()).as_unsigned().read()).write(42_u64); + assert!(((((*x.borrow()) == 42_i64) as i32) != 0)); + return 0; +} diff --git a/tests/unit/out/refcount/union_tagged_struct_arms.rs b/tests/unit/out/refcount/union_tagged_struct_arms.rs new file mode 100644 index 00000000..6c4cc0d5 --- /dev/null +++ b/tests/unit/out/refcount/union_tagged_struct_arms.rs @@ -0,0 +1,251 @@ +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(Clone, Copy, PartialEq, Debug, Default)] +enum Choice_enum { + #[default] + C_LIST = 1, + C_LETTERS = 2, + C_INTEGERS = 3, +} +impl From for Choice_enum { + fn from(n: i32) -> Choice_enum { + match n { + 1 => Choice_enum::C_LIST, + 2 => Choice_enum::C_LETTERS, + 3 => Choice_enum::C_INTEGERS, + _ => panic!("invalid Choice_enum value: {}", n), + } + } +} +libcc2rs::impl_enum_inc_dec!(Choice_enum); +#[derive(Default)] +pub struct anon_1 { + pub items: Value>>, + pub count: Value, + pub cursor: Value, +} +impl ByteRepr for anon_1 {} +#[derive(Default)] +pub struct anon_2 { + pub lo: Value, + pub hi: Value, + pub curr: Value, + pub step: Value, +} +impl ByteRepr for anon_2 { + fn to_bytes(&self, buf: &mut [u8]) { + (*self.lo.borrow()).to_bytes(&mut buf[0..4]); + (*self.hi.borrow()).to_bytes(&mut buf[4..8]); + (*self.curr.borrow()).to_bytes(&mut buf[8..12]); + (*self.step.borrow()).to_bytes(&mut buf[12..13]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + lo: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + hi: Rc::new(RefCell::new(::from_bytes(&buf[4..8]))), + curr: Rc::new(RefCell::new(::from_bytes(&buf[8..12]))), + step: Rc::new(RefCell::new(::from_bytes(&buf[12..13]))), + } + } +} +#[derive(Default)] +pub struct anon_3 { + pub lo: Value, + pub hi: Value, + pub curr: Value, + pub step: Value, + pub width: Value, +} +impl ByteRepr for anon_3 { + fn to_bytes(&self, buf: &mut [u8]) { + (*self.lo.borrow()).to_bytes(&mut buf[0..8]); + (*self.hi.borrow()).to_bytes(&mut buf[8..16]); + (*self.curr.borrow()).to_bytes(&mut buf[16..24]); + (*self.step.borrow()).to_bytes(&mut buf[24..32]); + (*self.width.borrow()).to_bytes(&mut buf[32..36]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + lo: Rc::new(RefCell::new(::from_bytes(&buf[0..8]))), + hi: Rc::new(RefCell::new(::from_bytes(&buf[8..16]))), + curr: Rc::new(RefCell::new(::from_bytes(&buf[16..24]))), + step: Rc::new(RefCell::new(::from_bytes(&buf[24..32]))), + width: Rc::new(RefCell::new(::from_bytes(&buf[32..36]))), + } + } +} +#[derive(Clone)] +pub struct anon_0 { + __store: libcc2rs::UnionStorage, +} +impl anon_0 { + pub fn list(&self) -> Ptr { + self.__store.reinterpret(0) + } + pub fn letters(&self) -> Ptr { + self.__store.reinterpret(0) + } + pub fn integers(&self) -> Ptr { + self.__store.reinterpret(0) + } +} +impl Default for anon_0 { + fn default() -> Self { + anon_0 { + __store: libcc2rs::UnionStorage::new(40), + } + } +} +impl ByteRepr for anon_0 {} +#[derive(Default)] +pub struct Branch { + pub choice: Value, + pub index: Value, + pub v: Value, +} +impl ByteRepr for Branch {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + thread_local!( + static items_4: Value]>> = Rc::new(RefCell::new(Box::new([ + Ptr::from_string_literal(b"a"), + Ptr::from_string_literal(b"b"), + Ptr::from_string_literal(b"c"), + ]))); + ); + let p_list: Value = >::default(); + (*(*p_list.borrow()).choice.borrow_mut()) = Choice_enum::C_LIST; + (*(*p_list.borrow()).index.borrow_mut()) = 0; + (*(*(*(*p_list.borrow()).v.borrow()).list().upgrade().deref()) + .items + .borrow_mut()) = (items_4.with(Value::clone).as_pointer() as Ptr>); + (*(*(*(*p_list.borrow()).v.borrow()).list().upgrade().deref()) + .count + .borrow_mut()) = 3_i64; + (*(*(*(*p_list.borrow()).v.borrow()).list().upgrade().deref()) + .cursor + .borrow_mut()) = 1_i64; + assert!( + ((((*(*(*(*p_list.borrow()).v.borrow()).list().upgrade().deref()) + .count + .borrow()) + == 3_i64) as i32) + != 0) + ); + assert!( + (((((((*(*(*(*p_list.borrow()).v.borrow()).list().upgrade().deref()) + .items + .borrow()) + .offset((1) as isize) + .read()) + .offset((0) as isize) + .read()) as i32) + == ('b' as i32)) as i32) + != 0) + ); + let p_letters: Value = >::default(); + (*(*p_letters.borrow()).choice.borrow_mut()) = Choice_enum::C_LETTERS; + (*(*p_letters.borrow()).index.borrow_mut()) = 1; + (*(*(*(*p_letters.borrow()).v.borrow()) + .letters() + .upgrade() + .deref()) + .lo + .borrow_mut()) = ('a' as i32); + (*(*(*(*p_letters.borrow()).v.borrow()) + .letters() + .upgrade() + .deref()) + .hi + .borrow_mut()) = ('z' as i32); + (*(*(*(*p_letters.borrow()).v.borrow()) + .letters() + .upgrade() + .deref()) + .curr + .borrow_mut()) = ('m' as i32); + (*(*(*(*p_letters.borrow()).v.borrow()) + .letters() + .upgrade() + .deref()) + .step + .borrow_mut()) = 1_u8; + assert!( + (((((*(*(*(*p_letters.borrow()).v.borrow()) + .letters() + .upgrade() + .deref()) + .hi + .borrow()) + - (*(*(*(*p_letters.borrow()).v.borrow()) + .letters() + .upgrade() + .deref()) + .lo + .borrow())) + == 25) as i32) + != 0) + ); + let p_integers: Value = >::default(); + (*(*p_integers.borrow()).choice.borrow_mut()) = Choice_enum::C_INTEGERS; + (*(*p_integers.borrow()).index.borrow_mut()) = 2; + (*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .lo + .borrow_mut()) = 1_i64; + (*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .hi + .borrow_mut()) = 100_i64; + (*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .curr + .borrow_mut()) = 1_i64; + (*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .step + .borrow_mut()) = 1_i64; + (*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .width + .borrow_mut()) = 3; + assert!( + ((((*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .hi + .borrow()) + == 100_i64) as i32) + != 0) + ); + assert!( + ((((*(*(*(*p_integers.borrow()).v.borrow()) + .integers() + .upgrade() + .deref()) + .width + .borrow()) + == 3) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/tag_vs_identifier_collision.c b/tests/unit/tag_vs_identifier_collision.c index 79212c64..a51731e9 100644 --- a/tests/unit/tag_vs_identifier_collision.c +++ b/tests/unit/tag_vs_identifier_collision.c @@ -1,4 +1,3 @@ -// no-compile: refcount #include typedef enum { MODE_IDLE, MODE_ACTIVE, MODE_DONE } widget; diff --git a/tests/unit/union_basic.c b/tests/unit/union_basic.c index 86731cc2..5d26a336 100644 --- a/tests/unit/union_basic.c +++ b/tests/unit/union_basic.c @@ -1,4 +1,3 @@ -// no-compile: refcount #include union basic { diff --git a/tests/unit/union_pointer_pun_writethrough.c b/tests/unit/union_pointer_pun_writethrough.c index d29cbc50..44bd842f 100644 --- a/tests/unit/union_pointer_pun_writethrough.c +++ b/tests/unit/union_pointer_pun_writethrough.c @@ -1,4 +1,4 @@ -// no-compile: refcount +// panic: refcount #include int main(void) { diff --git a/tests/unit/union_tagged_struct_arms.c b/tests/unit/union_tagged_struct_arms.c index f5b90e18..ca20e9a6 100644 --- a/tests/unit/union_tagged_struct_arms.c +++ b/tests/unit/union_tagged_struct_arms.c @@ -1,4 +1,4 @@ -// no-compile: refcount +// panic: refcount #include #include From 536b1c62dc632ce1b51bb25354d7b2b9770d8ce7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 13:23:33 +0100 Subject: [PATCH 06/14] Delete union.rs --- libcc2rs/src/lib.rs | 3 --- libcc2rs/src/rc.rs | 9 --------- libcc2rs/src/union.rs | 34 ---------------------------------- 3 files changed, 46 deletions(-) delete mode 100644 libcc2rs/src/union.rs diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index 88c73e74..4288464f 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -7,9 +7,6 @@ pub use reinterpret::ByteRepr; mod rc; pub use rc::*; -mod union; -pub use union::*; - mod fn_ptr; pub use fn_ptr::FnPtr; diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index 71f0c993..ccf2f991 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1246,15 +1246,6 @@ impl AsPointerDyn for Rc> { impl ByteRepr for Ptr {} -impl Ptr { - pub(crate) fn reinterpreted(alloc: Rc, byte_offset: usize) -> Self { - Ptr { - offset: byte_offset, - kind: PtrKind::Reinterpreted(alloc), - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/libcc2rs/src/union.rs b/libcc2rs/src/union.rs deleted file mode 100644 index 2501ebbf..00000000 --- a/libcc2rs/src/union.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2022-present INESC-ID. -// Distributed under the MIT license that can be found in the LICENSE file. - -use std::{cell::RefCell, rc::Rc}; - -use crate::Ptr; -use crate::reinterpret::{ByteRepr, OriginalAlloc, SliceOriginalAlloc}; - -pub struct UnionStorage { - bytes: Rc>>, -} - -impl UnionStorage { - pub fn new(size: usize) -> Self { - UnionStorage { - bytes: Rc::new(RefCell::new(vec![0u8; size])), - } - } - - pub fn reinterpret(&self, offset: usize) -> Ptr { - let alloc: Rc = Rc::new(SliceOriginalAlloc { - weak: Rc::downgrade(&self.bytes), - }); - Ptr::reinterpreted(alloc, offset) - } -} - -impl Clone for UnionStorage { - fn clone(&self) -> Self { - UnionStorage { - bytes: Rc::new(RefCell::new(self.bytes.borrow().clone())), - } - } -} From 25ca5310311465de6f8bc2b7eb970d499295dd82 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 13:24:05 +0100 Subject: [PATCH 07/14] Add Clone impl for union --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/converter.h | 2 +- .../converter/models/converter_refcount.cpp | 23 +++++++++++++++---- .../converter/models/converter_refcount.h | 2 +- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 2df856bc..2fa59e6d 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3907,7 +3907,7 @@ void Converter::AddOrdTrait(const clang::CXXRecordDecl *decl) { ConvertOrdAndPartialOrdTraits(decl, methods[0]); } -void Converter::AddCloneTrait(const clang::CXXRecordDecl *decl) {} +void Converter::AddCloneTrait(const clang::RecordDecl *decl) {} void Converter::AddDropTrait(const clang::CXXRecordDecl *decl) {} diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index fbdd4e59..9c3fc5f1 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -529,7 +529,7 @@ class Converter : public clang::RecursiveASTVisitor { std::string_view second_return, std::string_view record_name); - virtual void AddCloneTrait(const clang::CXXRecordDecl *decl); + virtual void AddCloneTrait(const clang::RecordDecl *decl); virtual void AddDropTrait(const clang::CXXRecordDecl *decl); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index cac005c1..1d044a3e 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -441,17 +441,30 @@ void ConverterRefCount::ConvertOrdAndPartialOrdTraits( second_return, GetRecordName(decl)); } -void ConverterRefCount::AddCloneTrait(const clang::CXXRecordDecl *decl) { - if (decl->defaultedCopyConstructorIsDeleted()) { +void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) { + auto record_name = GetRecordName(decl); + + if (decl->isUnion()) { + StrCat(std::format("impl Clone for {}", record_name)); + PushBrace impl_brace(*this); + StrCat("fn clone(&self) -> Self"); + PushBrace fn_brace(*this); + StrCat( + std::format("{} {{ __bytes: " + "Rc::new(RefCell::new(self.__bytes.borrow().clone())) }}", + record_name)); return; } - auto record_name = GetRecordName(decl); + auto *cxx = clang::dyn_cast(decl); + if (!cxx || cxx->defaultedCopyConstructorIsDeleted()) { + return; + } StrCat(keyword::kImpl, "Clone for", record_name, '{'); StrCat("fn clone(&self) -> Self {"); - for (auto ctor : decl->ctors()) { + for (auto ctor : cxx->ctors()) { if (ctor->isCopyConstructor()) { PushConversionKind push(*this, ConversionKind::FullRefCount); ConvertCXXConstructorBody(ctor); @@ -503,6 +516,7 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { } } + AddCloneTrait(decl); AddDefaultTrait(decl); AddByteReprTrait(decl); } @@ -1861,7 +1875,6 @@ ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) { std::vector attrs; if (decl->isUnion()) { - attrs.emplace_back("Clone"); return attrs; } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index b35d71b3..17c69b22 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -35,7 +35,7 @@ class ConverterRefCount final : public Converter { void ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl, const clang::FunctionDecl *op) override; - void AddCloneTrait(const clang::CXXRecordDecl *decl) override; + void AddCloneTrait(const clang::RecordDecl *decl) override; void AddDropTrait(const clang::CXXRecordDecl *decl) override; From 06a379f714e3100365a945645c4c1e91ab834b9d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 13:25:41 +0100 Subject: [PATCH 08/14] Use byte array instead of byte vector --- cpp2rust/converter/models/converter_refcount.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 1d044a3e..b9f1edbf 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -487,7 +487,9 @@ void ConverterRefCount::AddDefaultTraitForUnion(const clang::RecordDecl *decl) { PushBrace impl_brace(*this); StrCat("fn default() -> Self"); PushBrace fn_brace(*this); - StrCat(std::format("{} {{ __store: libcc2rs::UnionStorage::new({}) }}", name, + StrCat(std::format("{} {{ __bytes: Rc::new(RefCell::new(vec![0u8; " + "{}].into_boxed_slice())) }}", + name, ctx_.getASTRecordLayout(decl).getSize().getQuantity())); } @@ -503,15 +505,15 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { } StrCat(")]"); - StrCat(std::format("pub struct {} {{ __store: libcc2rs::UnionStorage, }}", - name)); + StrCat(std::format("pub struct {} {{ __bytes: Value>, }}", name)); StrCat(std::format("impl {}", name)); { PushBrace impl_brace(*this); for (auto *field : decl->fields()) { StrCat(std::format( - "pub fn {}(&self) -> Ptr<{}> {{ self.__store.reinterpret(0) }}", + "pub fn {}(&self) -> Ptr<{}> {{ (self.__bytes.as_pointer() " + "as Ptr).reinterpret_cast() }}", GetNamedDeclAsString(field), Mapper::Map(field->getType()))); } } From 47664ea37a4b8dd632f620ee32f1d59bb755cc23 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 13:26:14 +0100 Subject: [PATCH 09/14] Update tests --- .../refcount/tag_vs_identifier_collision.rs | 41 +++++++++++-------- tests/unit/out/refcount/union_basic.rs | 17 +++++--- .../union_pointer_pun_writethrough.rs | 17 +++++--- .../out/refcount/union_tagged_struct_arms.rs | 19 ++++++--- 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/tests/unit/out/refcount/tag_vs_identifier_collision.rs b/tests/unit/out/refcount/tag_vs_identifier_collision.rs index 0623f19f..1fc738f5 100644 --- a/tests/unit/out/refcount/tag_vs_identifier_collision.rs +++ b/tests/unit/out/refcount/tag_vs_identifier_collision.rs @@ -47,42 +47,56 @@ impl ByteRepr for point_struct { } } } -#[derive(Clone)] +#[derive()] pub struct point { - __store: libcc2rs::UnionStorage, + __bytes: Value>, } impl point { pub fn whole(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() } pub fn half(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } +} +impl Clone for point { + fn clone(&self) -> Self { + point { + __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())), + } } } impl Default for point { fn default() -> Self { point { - __store: libcc2rs::UnionStorage::new(4), + __bytes: Rc::new(RefCell::new(vec![0u8; 4].into_boxed_slice())), } } } impl ByteRepr for point {} -#[derive(Clone)] +#[derive()] pub struct slot_union { - __store: libcc2rs::UnionStorage, + __bytes: Value>, } impl slot_union { pub fn i(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() } pub fn u(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } +} +impl Clone for slot_union { + fn clone(&self) -> Self { + slot_union { + __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())), + } } } impl Default for slot_union { fn default() -> Self { slot_union { - __store: libcc2rs::UnionStorage::new(4), + __bytes: Rc::new(RefCell::new(vec![0u8; 4].into_boxed_slice())), } } } @@ -150,12 +164,7 @@ fn main_0() -> i32 { let w: Value = >::default(); (*(*w.borrow()).id.borrow_mut()) = 7; (*(*w.borrow()).mode.borrow_mut()) = widget_enum::MODE_ACTIVE; - assert!( - (({ - let _w: Ptr = (w.as_pointer()); - is_active_0(_w) - }) != 0) - ); + assert!((({ is_active_0((w.as_pointer()),) }) != 0)); (*(*w.borrow()).mode.borrow_mut()) = widget_enum::MODE_DONE; assert!( (((((*(*w.borrow()).mode.borrow()) as u32) == ((widget_enum::MODE_DONE as i32) as u32)) diff --git a/tests/unit/out/refcount/union_basic.rs b/tests/unit/out/refcount/union_basic.rs index f6fe7b26..1d4b1aaa 100644 --- a/tests/unit/out/refcount/union_basic.rs +++ b/tests/unit/out/refcount/union_basic.rs @@ -6,22 +6,29 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -#[derive(Clone)] +#[derive()] pub struct basic { - __store: libcc2rs::UnionStorage, + __bytes: Value>, } impl basic { pub fn i(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() } pub fn f(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } +} +impl Clone for basic { + fn clone(&self) -> Self { + basic { + __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())), + } } } impl Default for basic { fn default() -> Self { basic { - __store: libcc2rs::UnionStorage::new(4), + __bytes: Rc::new(RefCell::new(vec![0u8; 4].into_boxed_slice())), } } } diff --git a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs index 6ff3165c..ba175e70 100644 --- a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs +++ b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs @@ -11,22 +11,29 @@ pub fn main() { } fn main_0() -> i32 { let x: Value = Rc::new(RefCell::new((-1_i32 as i64))); - #[derive(Clone)] + #[derive()] pub struct anon_0 { - __store: libcc2rs::UnionStorage, + __bytes: Value>, } impl anon_0 { pub fn as_unsigned(&self) -> Ptr> { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() } pub fn as_signed(&self) -> Ptr> { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } + } + impl Clone for anon_0 { + fn clone(&self) -> Self { + anon_0 { + __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())), + } } } impl Default for anon_0 { fn default() -> Self { anon_0 { - __store: libcc2rs::UnionStorage::new(8), + __bytes: Rc::new(RefCell::new(vec![0u8; 8].into_boxed_slice())), } } } diff --git a/tests/unit/out/refcount/union_tagged_struct_arms.rs b/tests/unit/out/refcount/union_tagged_struct_arms.rs index 6c4cc0d5..1d308046 100644 --- a/tests/unit/out/refcount/union_tagged_struct_arms.rs +++ b/tests/unit/out/refcount/union_tagged_struct_arms.rs @@ -80,25 +80,32 @@ impl ByteRepr for anon_3 { } } } -#[derive(Clone)] +#[derive()] pub struct anon_0 { - __store: libcc2rs::UnionStorage, + __bytes: Value>, } impl anon_0 { pub fn list(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() } pub fn letters(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() } pub fn integers(&self) -> Ptr { - self.__store.reinterpret(0) + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } +} +impl Clone for anon_0 { + fn clone(&self) -> Self { + anon_0 { + __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())), + } } } impl Default for anon_0 { fn default() -> Self { anon_0 { - __store: libcc2rs::UnionStorage::new(40), + __bytes: Rc::new(RefCell::new(vec![0u8; 40].into_boxed_slice())), } } } From 5d3f9d6fe73b775c902321756e924fa63e94c830 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 13:40:22 +0100 Subject: [PATCH 10/14] Extract emission of accessor in separate function --- .../converter/models/converter_refcount.cpp | 43 ++++++++++--------- .../converter/models/converter_refcount.h | 2 + 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index b9f1edbf..07b8c7c1 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1493,6 +1493,28 @@ bool ConverterRefCount::VisitInitListExpr(clang::InitListExpr *expr) { return false; } +void ConverterRefCount::ConvertUnionMemberAccessor(clang::MemberExpr *expr) { + std::string str; + { + Buffer buf(*this); + PushExprKind push(*this, isLValue() ? ExprKind::LValue : ExprKind::RValue); + Converter::ConvertMemberExpr(expr); + str = std::move(buf).str(); + } + str += "()"; + + if (isAddrOf()) { + StrCat(str); + computed_expr_type_ = ComputedExprType::Pointer; + return; + } + if (isLValue()) { + pending_deref_.set(str); + return; + } + StrCat(DerefPtrExpr(str, expr->getMemberDecl()->getType())); +} + bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) { auto *member = expr->getMemberDecl(); bool known = Mapper::Contains(expr); @@ -1515,26 +1537,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) { if (auto *parent = clang::dyn_cast(member->getDeclContext()); parent && parent->isUnion() && clang::isa(member)) { - std::string str; - { - Buffer buf(*this); - PushExprKind push(*this, - isLValue() ? ExprKind::LValue : ExprKind::RValue); - Converter::ConvertMemberExpr(expr); - str = std::move(buf).str(); - } - str += "()"; - - if (isAddrOf()) { - StrCat(str); - computed_expr_type_ = ComputedExprType::Pointer; - return false; - } - if (isLValue()) { - pending_deref_.set(str); - return false; - } - StrCat(DerefPtrExpr(str, member->getType())); + ConvertUnionMemberAccessor(expr); return false; } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 17c69b22..1a3634a3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -105,6 +105,8 @@ class ConverterRefCount final : public Converter { bool VisitMemberExpr(clang::MemberExpr *expr) override; + void ConvertUnionMemberAccessor(clang::MemberExpr *expr); + bool VisitCXXNewExpr(clang::CXXNewExpr *expr) override; bool VisitCXXDeleteExpr(clang::CXXDeleteExpr *expr) override; From 891de44a437a0c46d2a490146ecb55b3a4221235 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 13:45:52 +0100 Subject: [PATCH 11/14] Delete empty derives --- cpp2rust/converter/models/converter_refcount.cpp | 7 +------ tests/unit/out/refcount/tag_vs_identifier_collision.rs | 2 -- tests/unit/out/refcount/union_basic.rs | 1 - tests/unit/out/refcount/union_pointer_pun_writethrough.rs | 1 - tests/unit/out/refcount/union_tagged_struct_arms.rs | 1 - 5 files changed, 1 insertion(+), 11 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 07b8c7c1..dc050b1a 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -499,13 +499,8 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { auto attrs = GetStructAttributes(decl); Mapper::SetDerives(ctx_.getCanonicalTagType(decl), std::vector(attrs.begin(), attrs.end())); - StrCat("#[derive("); - for (auto *attr : attrs) { - StrCat(attr, ','); - } - StrCat(")]"); - StrCat(std::format("pub struct {} {{ __bytes: Value>, }}", name)); + StrCat(std::format("pub struct {} {{ __bytes: Value> }}", name)); StrCat(std::format("impl {}", name)); { diff --git a/tests/unit/out/refcount/tag_vs_identifier_collision.rs b/tests/unit/out/refcount/tag_vs_identifier_collision.rs index 1fc738f5..514d1ba2 100644 --- a/tests/unit/out/refcount/tag_vs_identifier_collision.rs +++ b/tests/unit/out/refcount/tag_vs_identifier_collision.rs @@ -47,7 +47,6 @@ impl ByteRepr for point_struct { } } } -#[derive()] pub struct point { __bytes: Value>, } @@ -74,7 +73,6 @@ impl Default for point { } } impl ByteRepr for point {} -#[derive()] pub struct slot_union { __bytes: Value>, } diff --git a/tests/unit/out/refcount/union_basic.rs b/tests/unit/out/refcount/union_basic.rs index 1d4b1aaa..d2147430 100644 --- a/tests/unit/out/refcount/union_basic.rs +++ b/tests/unit/out/refcount/union_basic.rs @@ -6,7 +6,6 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -#[derive()] pub struct basic { __bytes: Value>, } diff --git a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs index ba175e70..c57fe3a0 100644 --- a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs +++ b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs @@ -11,7 +11,6 @@ pub fn main() { } fn main_0() -> i32 { let x: Value = Rc::new(RefCell::new((-1_i32 as i64))); - #[derive()] pub struct anon_0 { __bytes: Value>, } diff --git a/tests/unit/out/refcount/union_tagged_struct_arms.rs b/tests/unit/out/refcount/union_tagged_struct_arms.rs index 1d308046..d41cbc62 100644 --- a/tests/unit/out/refcount/union_tagged_struct_arms.rs +++ b/tests/unit/out/refcount/union_tagged_struct_arms.rs @@ -80,7 +80,6 @@ impl ByteRepr for anon_3 { } } } -#[derive()] pub struct anon_0 { __bytes: Value>, } From 5f316caebd36f2cbdd90abe9fdfa84f42ca7546d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 14:00:22 +0100 Subject: [PATCH 12/14] Always emit repr(C) for unsafe unions --- cpp2rust/converter/converter.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 2fa59e6d..98abbbaa 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -822,9 +822,7 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { } void Converter::EmitRustUnion(clang::RecordDecl *decl) { - if (EmitsReprCForRecords()) { - StrCat("#[repr(C)]"); - } + StrCat("#[repr(C)]"); auto attrs = GetStructAttributes(decl); Mapper::SetDerives(ctx_.getCanonicalTagType(decl), std::vector(attrs.begin(), attrs.end())); From 1e10aa4777ca430151f7a9f0069e6c79dbf4bffd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 15:17:03 +0100 Subject: [PATCH 13/14] Update tests --- .../refcount/tag_vs_identifier_collision.rs | 29 ++++++++++++++++++- .../out/refcount/union_tagged_struct_arms.rs | 8 +++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/unit/out/refcount/tag_vs_identifier_collision.rs b/tests/unit/out/refcount/tag_vs_identifier_collision.rs index 514d1ba2..aeee8773 100644 --- a/tests/unit/out/refcount/tag_vs_identifier_collision.rs +++ b/tests/unit/out/refcount/tag_vs_identifier_collision.rs @@ -24,12 +24,31 @@ impl From for widget_enum { } } libcc2rs::impl_enum_inc_dec!(widget_enum); +impl ByteRepr for widget_enum { + fn to_bytes(&self, buf: &mut [u8]) { + (*self as i32).to_bytes(buf); + } + fn from_bytes(buf: &[u8]) -> Self { + ::from(i32::from_bytes(buf)) + } +} #[derive(Default)] pub struct widget { pub id: Value, pub mode: Value, } -impl ByteRepr for widget {} +impl ByteRepr for widget { + fn to_bytes(&self, buf: &mut [u8]) { + (*self.id.borrow()).to_bytes(&mut buf[0..4]); + (*self.mode.borrow()).to_bytes(&mut buf[4..8]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + id: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + mode: Rc::new(RefCell::new(::from_bytes(&buf[4..8]))), + } + } +} #[derive(Default)] pub struct point_struct { pub x: Value, @@ -115,6 +134,14 @@ impl From for slot { } } libcc2rs::impl_enum_inc_dec!(slot); +impl ByteRepr for slot { + fn to_bytes(&self, buf: &mut [u8]) { + (*self as i32).to_bytes(buf); + } + fn from_bytes(buf: &[u8]) -> Self { + ::from(i32::from_bytes(buf)) + } +} #[derive(Default)] pub struct Inner { pub tag_field: Value, diff --git a/tests/unit/out/refcount/union_tagged_struct_arms.rs b/tests/unit/out/refcount/union_tagged_struct_arms.rs index d41cbc62..112a9053 100644 --- a/tests/unit/out/refcount/union_tagged_struct_arms.rs +++ b/tests/unit/out/refcount/union_tagged_struct_arms.rs @@ -24,6 +24,14 @@ impl From for Choice_enum { } } libcc2rs::impl_enum_inc_dec!(Choice_enum); +impl ByteRepr for Choice_enum { + fn to_bytes(&self, buf: &mut [u8]) { + (*self as i32).to_bytes(buf); + } + fn from_bytes(buf: &[u8]) -> Self { + ::from(i32::from_bytes(buf)) + } +} #[derive(Default)] pub struct anon_1 { pub items: Value>>, From 93e6a85f98085821deaba2fb4282a7f51889a5f3 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 25 Jun 2026 15:46:27 +0100 Subject: [PATCH 14/14] Use Box::from and evit avoid extra std::format's --- .../converter/models/converter_refcount.cpp | 19 ++++++++----------- .../refcount/tag_vs_identifier_collision.rs | 4 ++-- tests/unit/out/refcount/union_basic.rs | 2 +- .../union_pointer_pun_writethrough.rs | 2 +- .../out/refcount/union_tagged_struct_arms.rs | 2 +- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index dc050b1a..eb89d496 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -445,14 +445,12 @@ void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) { auto record_name = GetRecordName(decl); if (decl->isUnion()) { - StrCat(std::format("impl Clone for {}", record_name)); + StrCat("impl Clone for", record_name); PushBrace impl_brace(*this); StrCat("fn clone(&self) -> Self"); PushBrace fn_brace(*this); - StrCat( - std::format("{} {{ __bytes: " - "Rc::new(RefCell::new(self.__bytes.borrow().clone())) }}", - record_name)); + StrCat(record_name, + "{ __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())) }"); return; } @@ -483,14 +481,13 @@ void ConverterRefCount::AddDefaultTrait(const clang::RecordDecl *decl) { void ConverterRefCount::AddDefaultTraitForUnion(const clang::RecordDecl *decl) { auto name = GetRecordName(decl); - StrCat(std::format("impl Default for {}", name)); + StrCat("impl Default for", name); PushBrace impl_brace(*this); StrCat("fn default() -> Self"); PushBrace fn_brace(*this); - StrCat(std::format("{} {{ __bytes: Rc::new(RefCell::new(vec![0u8; " - "{}].into_boxed_slice())) }}", - name, - ctx_.getASTRecordLayout(decl).getSize().getQuantity())); + StrCat(std::format( + "{} {{ __bytes: Rc::new(RefCell::new(Box::from([0u8; {}]))) }}", name, + ctx_.getASTRecordLayout(decl).getSize().getQuantity())); } void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { @@ -502,7 +499,7 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { StrCat(std::format("pub struct {} {{ __bytes: Value> }}", name)); - StrCat(std::format("impl {}", name)); + StrCat("impl", name); { PushBrace impl_brace(*this); for (auto *field : decl->fields()) { diff --git a/tests/unit/out/refcount/tag_vs_identifier_collision.rs b/tests/unit/out/refcount/tag_vs_identifier_collision.rs index aeee8773..eccceda4 100644 --- a/tests/unit/out/refcount/tag_vs_identifier_collision.rs +++ b/tests/unit/out/refcount/tag_vs_identifier_collision.rs @@ -87,7 +87,7 @@ impl Clone for point { impl Default for point { fn default() -> Self { point { - __bytes: Rc::new(RefCell::new(vec![0u8; 4].into_boxed_slice())), + __bytes: Rc::new(RefCell::new(Box::from([0u8; 4]))), } } } @@ -113,7 +113,7 @@ impl Clone for slot_union { impl Default for slot_union { fn default() -> Self { slot_union { - __bytes: Rc::new(RefCell::new(vec![0u8; 4].into_boxed_slice())), + __bytes: Rc::new(RefCell::new(Box::from([0u8; 4]))), } } } diff --git a/tests/unit/out/refcount/union_basic.rs b/tests/unit/out/refcount/union_basic.rs index d2147430..9988b7b7 100644 --- a/tests/unit/out/refcount/union_basic.rs +++ b/tests/unit/out/refcount/union_basic.rs @@ -27,7 +27,7 @@ impl Clone for basic { impl Default for basic { fn default() -> Self { basic { - __bytes: Rc::new(RefCell::new(vec![0u8; 4].into_boxed_slice())), + __bytes: Rc::new(RefCell::new(Box::from([0u8; 4]))), } } } diff --git a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs index c57fe3a0..12c36840 100644 --- a/tests/unit/out/refcount/union_pointer_pun_writethrough.rs +++ b/tests/unit/out/refcount/union_pointer_pun_writethrough.rs @@ -32,7 +32,7 @@ fn main_0() -> i32 { impl Default for anon_0 { fn default() -> Self { anon_0 { - __bytes: Rc::new(RefCell::new(vec![0u8; 8].into_boxed_slice())), + __bytes: Rc::new(RefCell::new(Box::from([0u8; 8]))), } } } diff --git a/tests/unit/out/refcount/union_tagged_struct_arms.rs b/tests/unit/out/refcount/union_tagged_struct_arms.rs index 112a9053..37417848 100644 --- a/tests/unit/out/refcount/union_tagged_struct_arms.rs +++ b/tests/unit/out/refcount/union_tagged_struct_arms.rs @@ -112,7 +112,7 @@ impl Clone for anon_0 { impl Default for anon_0 { fn default() -> Self { anon_0 { - __bytes: Rc::new(RefCell::new(vec![0u8; 40].into_boxed_slice())), + __bytes: Rc::new(RefCell::new(Box::from([0u8; 40]))), } } }