From 27ea512313a101ffb02eb9c24587dea91b0ae6a9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 6 Jul 2026 15:15:00 +0100 Subject: [PATCH] Add Clone impl for C structs --- cpp2rust/converter/converter.cpp | 2 +- .../converter/models/converter_refcount.cpp | 17 ++++- .../out/refcount/cross_tu_tag_collision.rs | 7 ++ .../out/refcount/opaque_forward_decl.rs | 8 ++ tests/unit/c_struct.c | 6 ++ tests/unit/out/refcount/anonymous-struct_c.rs | 74 +++++++++++++++++++ tests/unit/out/refcount/anonymous_enum_c.rs | 15 ++++ tests/unit/out/refcount/array_const_init.rs | 9 +++ tests/unit/out/refcount/c_struct.rs | 46 ++++++++++++ .../out/refcount/enum_default_in_static.rs | 8 ++ tests/unit/out/refcount/enum_int_interop_c.rs | 9 +++ .../out/refcount/goto_aggregate_default.rs | 8 ++ .../refcount/local_anon_struct_collision.rs | 16 ++++ .../unit/out/refcount/memcpy_struct_bytes.rs | 8 ++ .../unit/out/refcount/opaque_forward_decl.rs | 8 ++ .../unit/out/refcount/opaque_then_defined.rs | 16 ++++ .../refcount/tag_vs_identifier_collision.rs | 37 ++++++++++ .../out/refcount/union_addrof_external.rs | 17 +++++ .../unit/out/refcount/union_cross_arm_cast.rs | 27 +++++++ .../out/refcount/union_field_alignment.rs | 8 ++ .../unit/out/refcount/union_memset_memcpy.rs | 25 +++++++ tests/unit/out/refcount/union_nested.rs | 26 +++++++ .../out/refcount/union_pointer_pun_address.rs | 15 ++++ .../out/refcount/union_struct_dual_use.rs | 15 ++++ .../out/refcount/union_tagged_many_arms.rs | 8 ++ .../unit/out/refcount/union_tagged_simple.rs | 9 +++ .../out/refcount/union_tagged_struct_arms.rs | 39 ++++++++++ .../refcount/union_void_ptr_sized_deref.rs | 8 ++ .../out/refcount/va_arg_non_primitive_ptrs.rs | 8 ++ tests/unit/out/refcount/va_arg_struct_ctx.rs | 8 ++ tests/unit/out/unsafe/c_struct.rs | 5 ++ 31 files changed, 510 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 37f474ad..eb513e84 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -819,9 +819,9 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { // Traits if (auto *cxx = clang::dyn_cast(decl)) { AddOrdTrait(cxx); - AddCloneTrait(cxx); AddDropTrait(cxx); } + AddCloneTrait(decl); AddDefaultTrait(decl); AddByteReprTrait(decl); } diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index c7a2be2b..34f25d62 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -465,7 +465,22 @@ void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) { } auto *cxx = clang::dyn_cast(decl); - if (!cxx || cxx->defaultedCopyConstructorIsDeleted()) { + if (!cxx) { + StrCat(keyword::kImpl, "Clone for", record_name); + PushBrace impl_brace(*this); + StrCat("fn clone(&self) -> Self"); + PushBrace fn_brace(*this); + StrCat("Self"); + PushBrace init_brace(*this); + for (auto *field : decl->fields()) { + auto name = GetNamedDeclAsString(field); + StrCat(std::format( + "{0}: Rc::new(RefCell::new((*self.{0}.borrow()).clone())),", name)); + } + return; + } + + if (cxx->defaultedCopyConstructorIsDeleted()) { return; } diff --git a/tests/multi-file/cross_tu_tag_collision/out/refcount/cross_tu_tag_collision.rs b/tests/multi-file/cross_tu_tag_collision/out/refcount/cross_tu_tag_collision.rs index 981fa9ae..f4846e89 100644 --- a/tests/multi-file/cross_tu_tag_collision/out/refcount/cross_tu_tag_collision.rs +++ b/tests/multi-file/cross_tu_tag_collision/out/refcount/cross_tu_tag_collision.rs @@ -10,6 +10,13 @@ use std::rc::{Rc, Weak}; pub struct widget { pub id: Value, } +impl Clone for widget { + fn clone(&self) -> Self { + Self { + id: Rc::new(RefCell::new((*self.id.borrow()).clone())), + } + } +} impl ByteRepr for widget { fn byte_size() -> usize { 4 diff --git a/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs b/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs index 77d2b14b..fe2518b9 100644 --- a/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs +++ b/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs @@ -11,6 +11,14 @@ pub struct container { pub p: Value>, pub x: Value, } +impl Clone for container { + fn clone(&self) -> Self { + Self { + p: Rc::new(RefCell::new((*self.p.borrow()).clone())), + x: Rc::new(RefCell::new((*self.x.borrow()).clone())), + } + } +} impl ByteRepr for container { fn byte_size() -> usize { 16 diff --git a/tests/unit/c_struct.c b/tests/unit/c_struct.c index 9e79b582..cea796c1 100644 --- a/tests/unit/c_struct.c +++ b/tests/unit/c_struct.c @@ -29,6 +29,12 @@ int main() { assert(p.x == 10); assert(p.y == 20); + struct Point q = p; + q.x = 99; + assert(p.x == 10); + assert(q.x == 99); + assert(q.y == 20); + struct Line l = {{1, 2}, {3, 4}}; assert(l.start.x == 1); assert(l.end.y == 4); diff --git a/tests/unit/out/refcount/anonymous-struct_c.rs b/tests/unit/out/refcount/anonymous-struct_c.rs index 970d6e99..065fd2cc 100644 --- a/tests/unit/out/refcount/anonymous-struct_c.rs +++ b/tests/unit/out/refcount/anonymous-struct_c.rs @@ -11,6 +11,14 @@ pub struct Named { pub a: Value, pub b: Value, } +impl Clone for Named { + fn clone(&self) -> Self { + Self { + a: Rc::new(RefCell::new((*self.a.borrow()).clone())), + b: Rc::new(RefCell::new((*self.b.borrow()).clone())), + } + } +} impl ByteRepr for Named { fn byte_size() -> usize { 8 @@ -31,6 +39,14 @@ pub struct anon_0 { pub c: Value, pub d: Value, } +impl Clone for anon_0 { + fn clone(&self) -> Self { + Self { + c: Rc::new(RefCell::new((*self.c.borrow()).clone())), + d: Rc::new(RefCell::new((*self.d.borrow()).clone())), + } + } +} impl ByteRepr for anon_0 { fn byte_size() -> usize { 8 @@ -51,6 +67,14 @@ pub struct anon_1 { pub g: Value, pub h: Value, } +impl Clone for anon_1 { + fn clone(&self) -> Self { + Self { + g: Rc::new(RefCell::new((*self.g.borrow()).clone())), + h: Rc::new(RefCell::new((*self.h.borrow()).clone())), + } + } +} impl ByteRepr for anon_1 { fn byte_size() -> usize { 8 @@ -71,6 +95,14 @@ pub struct anon_2 { pub e: Value, pub f: Value, } +impl Clone for anon_2 { + fn clone(&self) -> Self { + Self { + e: Rc::new(RefCell::new((*self.e.borrow()).clone())), + f: Rc::new(RefCell::new((*self.f.borrow()).clone())), + } + } +} impl ByteRepr for anon_2 { fn byte_size() -> usize { 8 @@ -90,6 +122,13 @@ impl ByteRepr for anon_2 { pub struct anon_4 { pub j: Value, } +impl Clone for anon_4 { + fn clone(&self) -> Self { + Self { + j: Rc::new(RefCell::new((*self.j.borrow()).clone())), + } + } +} impl ByteRepr for anon_4 { fn byte_size() -> usize { 4 @@ -107,6 +146,13 @@ impl ByteRepr for anon_4 { pub struct anon_5 { pub k: Value, } +impl Clone for anon_5 { + fn clone(&self) -> Self { + Self { + k: Rc::new(RefCell::new((*self.k.borrow()).clone())), + } + } +} impl ByteRepr for anon_5 { fn byte_size() -> usize { 4 @@ -126,6 +172,15 @@ pub struct anon_3 { pub inner_named: Value, pub anon_5: Value, } +impl Clone for anon_3 { + fn clone(&self) -> Self { + Self { + i: Rc::new(RefCell::new((*self.i.borrow()).clone())), + inner_named: Rc::new(RefCell::new((*self.inner_named.borrow()).clone())), + anon_5: Rc::new(RefCell::new((*self.anon_5.borrow()).clone())), + } + } +} impl ByteRepr for anon_3 { fn byte_size() -> usize { 12 @@ -151,6 +206,17 @@ pub struct Outer { pub anon_2: Value, pub anon_3: Value, } +impl Clone for Outer { + fn clone(&self) -> Self { + Self { + named: Rc::new(RefCell::new((*self.named.borrow()).clone())), + anon0: Rc::new(RefCell::new((*self.anon0.borrow()).clone())), + anon1: Rc::new(RefCell::new((*self.anon1.borrow()).clone())), + anon_2: Rc::new(RefCell::new((*self.anon_2.borrow()).clone())), + anon_3: Rc::new(RefCell::new((*self.anon_3.borrow()).clone())), + } + } +} impl ByteRepr for Outer { fn byte_size() -> usize { 44 @@ -229,6 +295,14 @@ fn main_0() -> i32 { pub x: Value, pub z: Value, } + impl Clone for anon_6 { + fn clone(&self) -> Self { + Self { + x: Rc::new(RefCell::new((*self.x.borrow()).clone())), + z: Rc::new(RefCell::new((*self.z.borrow()).clone())), + } + } + } impl ByteRepr for anon_6 { fn byte_size() -> usize { 8 diff --git a/tests/unit/out/refcount/anonymous_enum_c.rs b/tests/unit/out/refcount/anonymous_enum_c.rs index a851a2b5..5aa61233 100644 --- a/tests/unit/out/refcount/anonymous_enum_c.rs +++ b/tests/unit/out/refcount/anonymous_enum_c.rs @@ -58,6 +58,13 @@ impl ByteRepr for anon_1 { pub struct S { pub a: Value, } +impl Clone for S { + fn clone(&self) -> Self { + Self { + a: Rc::new(RefCell::new((*self.a.borrow()).clone())), + } + } +} impl ByteRepr for S { fn byte_size() -> usize { 4 @@ -124,6 +131,14 @@ pub struct WithAnonField { pub a: Value, pub field: Value, } +impl Clone for WithAnonField { + fn clone(&self) -> Self { + Self { + a: Rc::new(RefCell::new((*self.a.borrow()).clone())), + field: Rc::new(RefCell::new((*self.field.borrow()).clone())), + } + } +} impl ByteRepr for WithAnonField { fn byte_size() -> usize { 8 diff --git a/tests/unit/out/refcount/array_const_init.rs b/tests/unit/out/refcount/array_const_init.rs index 7a2cf78d..b0e467de 100644 --- a/tests/unit/out/refcount/array_const_init.rs +++ b/tests/unit/out/refcount/array_const_init.rs @@ -12,6 +12,15 @@ pub struct S { pub tail: Value>, pub buf: Value>, } +impl Clone for S { + fn clone(&self) -> Self { + Self { + head: Rc::new(RefCell::new((*self.head.borrow()).clone())), + tail: Rc::new(RefCell::new((*self.tail.borrow()).clone())), + buf: Rc::new(RefCell::new((*self.buf.borrow()).clone())), + } + } +} impl Default for S { fn default() -> Self { S { diff --git a/tests/unit/out/refcount/c_struct.rs b/tests/unit/out/refcount/c_struct.rs index d6921e87..d70a7553 100644 --- a/tests/unit/out/refcount/c_struct.rs +++ b/tests/unit/out/refcount/c_struct.rs @@ -11,6 +11,14 @@ pub struct Point { pub x: Value, pub y: Value, } +impl Clone for Point { + fn clone(&self) -> Self { + Self { + x: Rc::new(RefCell::new((*self.x.borrow()).clone())), + y: Rc::new(RefCell::new((*self.y.borrow()).clone())), + } + } +} impl ByteRepr for Point { fn byte_size() -> usize { 8 @@ -31,6 +39,14 @@ pub struct Line { pub start: Value, pub end: Value, } +impl Clone for Line { + fn clone(&self) -> Self { + Self { + start: Rc::new(RefCell::new((*self.start.borrow()).clone())), + end: Rc::new(RefCell::new((*self.end.borrow()).clone())), + } + } +} impl ByteRepr for Line { fn byte_size() -> usize { 16 @@ -51,6 +67,14 @@ pub struct Node { pub value: Value, pub next: Value>, } +impl Clone for Node { + fn clone(&self) -> Self { + Self { + value: Rc::new(RefCell::new((*self.value.borrow()).clone())), + next: Rc::new(RefCell::new((*self.next.borrow()).clone())), + } + } +} impl ByteRepr for Node { fn byte_size() -> usize { 16 @@ -97,6 +121,14 @@ pub struct Inner { pub a: Value, pub b: Value, } +impl Clone for Inner { + fn clone(&self) -> Self { + Self { + a: Rc::new(RefCell::new((*self.a.borrow()).clone())), + b: Rc::new(RefCell::new((*self.b.borrow()).clone())), + } + } +} impl ByteRepr for Inner { fn byte_size() -> usize { 8 @@ -118,6 +150,15 @@ pub struct Container { pub color: Value, pub count: Value, } +impl Clone for Container { + fn clone(&self) -> Self { + Self { + inner: Rc::new(RefCell::new((*self.inner.borrow()).clone())), + color: Rc::new(RefCell::new((*self.color.borrow()).clone())), + count: Rc::new(RefCell::new((*self.count.borrow()).clone())), + } + } +} impl ByteRepr for Container { fn byte_size() -> usize { 16 @@ -145,6 +186,11 @@ fn main_0() -> i32 { })); assert!(((((*(*p.borrow()).x.borrow()) == 10) as i32) != 0)); assert!(((((*(*p.borrow()).y.borrow()) == 20) as i32) != 0)); + let q: Value = Rc::new(RefCell::new((*p.borrow()).clone())); + (*(*q.borrow()).x.borrow_mut()) = 99; + assert!(((((*(*p.borrow()).x.borrow()) == 10) as i32) != 0)); + assert!(((((*(*q.borrow()).x.borrow()) == 99) as i32) != 0)); + assert!(((((*(*q.borrow()).y.borrow()) == 20) as i32) != 0)); let l: Value = Rc::new(RefCell::new(Line { start: Rc::new(RefCell::new(Point { x: Rc::new(RefCell::new(1)), diff --git a/tests/unit/out/refcount/enum_default_in_static.rs b/tests/unit/out/refcount/enum_default_in_static.rs index 866240a2..16aac005 100644 --- a/tests/unit/out/refcount/enum_default_in_static.rs +++ b/tests/unit/out/refcount/enum_default_in_static.rs @@ -37,6 +37,14 @@ pub struct Config { pub count: Value, pub mode: Value, } +impl Clone for Config { + fn clone(&self) -> Self { + Self { + count: Rc::new(RefCell::new((*self.count.borrow()).clone())), + mode: Rc::new(RefCell::new((*self.mode.borrow()).clone())), + } + } +} impl ByteRepr for Config { fn byte_size() -> usize { 8 diff --git a/tests/unit/out/refcount/enum_int_interop_c.rs b/tests/unit/out/refcount/enum_int_interop_c.rs index e004cd47..c6945040 100644 --- a/tests/unit/out/refcount/enum_int_interop_c.rs +++ b/tests/unit/out/refcount/enum_int_interop_c.rs @@ -92,6 +92,15 @@ pub struct Entry { pub color: Value, pub opt: Value