From 71c3941379491189ee7caddfe91b576ea50ded92 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 16 May 2026 19:06:33 +0100 Subject: [PATCH 01/15] Only apply IsRedundantCopyInConversion for iterators --- cpp2rust/converter/converter_lib.cpp | 27 +++++++++++++++---- .../converter/models/converter_refcount.cpp | 4 ++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 040d7d1b..d83371a9 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -644,14 +644,31 @@ std::string GetClassName(clang::QualType type) { return {}; } +static bool IsIteratorType(clang::QualType qt) { + if (auto *record = qt->getAsCXXRecordDecl()) { + for (auto *d : record->decls()) { + if (auto *tnd = llvm::dyn_cast(d)) { + if (tnd->getName() == "iterator_category") + return true; + } + } + } + return false; +} + bool IsRedundantCopyInConversion(clang::ASTContext &ctx, const clang::CXXConstructExpr *expr) { - auto parents = ctx.getParentMapContext().getParents(*expr); - if (parents.empty()) { - return false; + if (auto parents = ctx.getParentMapContext().getParents(*expr); + !parents.empty()) { + if (auto *parent = parents[0].get()) { + if (parent->getConstructor()->isConvertingConstructor( + /* AllowExplicit= */ false)) { + return IsIteratorType(expr->getType()) && + IsIteratorType(parent->getType()); + } + } } - auto *parent = parents[0].get(); - return parent && parent->getConstructor()->isConvertingConstructor(false); + return false; } bool IsVaListType(clang::QualType type) { diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index c2bd9e18..fc1e0d27 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1564,7 +1564,9 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { } if (ctor->isCopyConstructor()) { - StrCat(ConvertRValue(expr->getArg(0))); + StrCat(IsRedundantCopyInConversion(ctx_, expr) + ? ConvertRValue(expr->getArg(0)) + : ConvertFreshRValue(expr->getArg(0))); return false; } From 7df8b07f8eda8aa29de5ec93dfb8a358b5b506f9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 17 May 2026 14:20:34 +0100 Subject: [PATCH 02/15] Add initialize rlist test --- tests/unit/initializer_list.cpp | 16 ++++++++++++ tests/unit/out/refcount/initializer_list.rs | 27 ++++++++++++++++++++ tests/unit/out/unsafe/initializer_list.rs | 28 +++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 tests/unit/initializer_list.cpp create mode 100644 tests/unit/out/refcount/initializer_list.rs create mode 100644 tests/unit/out/unsafe/initializer_list.rs diff --git a/tests/unit/initializer_list.cpp b/tests/unit/initializer_list.cpp new file mode 100644 index 00000000..96bcbc92 --- /dev/null +++ b/tests/unit/initializer_list.cpp @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +size_t f(std::initializer_list bytes) { + std::vector *buf = new std::vector(bytes); + size_t n = bytes.size(); + delete buf; + return n; +} + +int main() { + assert(f({1, 2, 3}) == 3); + return 0; +} diff --git a/tests/unit/out/refcount/initializer_list.rs b/tests/unit/out/refcount/initializer_list.rs new file mode 100644 index 00000000..08d0258e --- /dev/null +++ b/tests/unit/out/refcount/initializer_list.rs @@ -0,0 +1,27 @@ +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 f_0(bytes: Vec) -> u64 { + let bytes: Value> = Rc::new(RefCell::new(bytes)); + let buf: Value>> = Rc::new(RefCell::new(Ptr::alloc((*bytes.borrow())))); + let n: Value = Rc::new(RefCell::new((*bytes.borrow()).len() as u64)); + (*buf.borrow()).delete(); + return (*n.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _bytes: Vec = vec![1, 2, 3]; + f_0(_bytes) + }) == 3_u64) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/initializer_list.rs b/tests/unit/out/unsafe/initializer_list.rs new file mode 100644 index 00000000..22424290 --- /dev/null +++ b/tests/unit/out/unsafe/initializer_list.rs @@ -0,0 +1,28 @@ +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 f_0(mut bytes: Vec) -> u64 { + let mut buf: *mut Vec = (Box::leak(Box::new(bytes)) as *mut Vec); + let mut n: u64 = bytes.len() as u64; + ::std::mem::drop(Box::from_raw(buf)); + return n; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _bytes: Vec = vec![1, 2, 3]; + f_0(_bytes) + }) == (3_u64)) + ); + return 0; +} From d9719d12ca5b74c5d8bdef02964bdd4b22051242 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 17 May 2026 14:23:12 +0100 Subject: [PATCH 03/15] Update tests --- tests/unit/out/refcount/initializer_list.rs | 2 +- tests/unit/out/unsafe/initializer_list.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/out/refcount/initializer_list.rs b/tests/unit/out/refcount/initializer_list.rs index 08d0258e..94bf5756 100644 --- a/tests/unit/out/refcount/initializer_list.rs +++ b/tests/unit/out/refcount/initializer_list.rs @@ -8,7 +8,7 @@ use std::os::fd::AsFd; use std::rc::{Rc, Weak}; pub fn f_0(bytes: Vec) -> u64 { let bytes: Value> = Rc::new(RefCell::new(bytes)); - let buf: Value>> = Rc::new(RefCell::new(Ptr::alloc((*bytes.borrow())))); + let buf: Value>> = Rc::new(RefCell::new(Ptr::alloc((*bytes.borrow()).clone()))); let n: Value = Rc::new(RefCell::new((*bytes.borrow()).len() as u64)); (*buf.borrow()).delete(); return (*n.borrow()); diff --git a/tests/unit/out/unsafe/initializer_list.rs b/tests/unit/out/unsafe/initializer_list.rs index 22424290..dbec9d20 100644 --- a/tests/unit/out/unsafe/initializer_list.rs +++ b/tests/unit/out/unsafe/initializer_list.rs @@ -7,7 +7,7 @@ use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn f_0(mut bytes: Vec) -> u64 { - let mut buf: *mut Vec = (Box::leak(Box::new(bytes)) as *mut Vec); + let mut buf: *mut Vec = (Box::leak(Box::new(bytes.clone())) as *mut Vec); let mut n: u64 = bytes.len() as u64; ::std::mem::drop(Box::from_raw(buf)); return n; From ee959dac4bc5d5bf307fa0ea9e781f9fd331b7bc Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 17 May 2026 14:26:31 +0100 Subject: [PATCH 04/15] Update tests --- tests/unit/out/refcount/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/out/refcount/map.rs b/tests/unit/out/refcount/map.rs index 7af8fb5f..c48dad05 100644 --- a/tests/unit/out/refcount/map.rs +++ b/tests/unit/out/refcount/map.rs @@ -274,7 +274,7 @@ fn main_0() -> i32 { ); RefcountMapIter::erase( ((r).clone() as Ptr>>), - &(*it4.borrow()), + &(*it4.borrow()).clone(), ); assert!(((*r.upgrade().deref()).len() as u64 == 3_u64)); assert!( From 2c2bdab5444d78dc689962656ef59a3cc83ab806 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 10:16:05 +0100 Subject: [PATCH 05/15] Add redundant copy in conversion test --- .../refcount/redundant_copy_in_conversion.rs | 59 +++++++++++++++++++ .../unsafe/redundant_copy_in_conversion.rs | 43 ++++++++++++++ tests/unit/redundant_copy_in_conversion.cpp | 20 +++++++ 3 files changed, 122 insertions(+) create mode 100644 tests/unit/out/refcount/redundant_copy_in_conversion.rs create mode 100644 tests/unit/out/unsafe/redundant_copy_in_conversion.rs create mode 100644 tests/unit/redundant_copy_in_conversion.cpp diff --git a/tests/unit/out/refcount/redundant_copy_in_conversion.rs b/tests/unit/out/refcount/redundant_copy_in_conversion.rs new file mode 100644 index 00000000..d6d1492f --- /dev/null +++ b/tests/unit/out/refcount/redundant_copy_in_conversion.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}; +#[derive(Default)] +pub struct iter { + pub p: Value>, +} +impl Clone for iter { + fn clone(&self) -> Self { + let mut this = Self { + p: Rc::new(RefCell::new((*self.p.borrow()).clone())), + }; + this + } +} +impl ByteRepr for iter {} +#[derive(Default)] +pub struct const_iter { + pub p: Value>, +} +impl const_iter { + pub fn const_iter(o: Ptr) -> Self { + let mut this = Self { + p: Rc::new(RefCell::new((*(*o.upgrade().deref()).p.borrow()).clone())), + }; + this + } +} +impl Clone for const_iter { + fn clone(&self) -> Self { + let mut this = Self { + p: Rc::new(RefCell::new(Ptr::::null())), + }; + this + } +} +impl ByteRepr for const_iter {} +pub fn sink_0(i: const_iter) { + let i: Value = Rc::new(RefCell::new(i)); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let buf: Value> = Rc::new(RefCell::new(Box::new([0, 0]))); + let it: Value = Rc::new(RefCell::new(iter { + p: Rc::new(RefCell::new((buf.as_pointer() as Ptr))), + })); + ({ + let _i: const_iter = const_iter::const_iter(it.as_pointer()); + sink_0(_i) + }); + return 0; +} diff --git a/tests/unit/out/unsafe/redundant_copy_in_conversion.rs b/tests/unit/out/unsafe/redundant_copy_in_conversion.rs new file mode 100644 index 00000000..75fbe3d9 --- /dev/null +++ b/tests/unit/out/unsafe/redundant_copy_in_conversion.rs @@ -0,0 +1,43 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct iter { + pub p: *mut i32, +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct const_iter { + pub p: *const i32, +} +impl const_iter { + pub unsafe fn const_iter(o: *const iter) -> Self { + let mut this = Self { + p: (*o).p.cast_const(), + }; + this + } +} +pub unsafe fn sink_0(mut i: const_iter) {} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut buf: [i32; 2] = [0, 0]; + let mut it: iter = iter { + p: buf.as_mut_ptr(), + }; + (unsafe { + let _i: const_iter = const_iter::const_iter(&it as *const iter); + sink_0(_i) + }); + return 0; +} diff --git a/tests/unit/redundant_copy_in_conversion.cpp b/tests/unit/redundant_copy_in_conversion.cpp new file mode 100644 index 00000000..9150cacf --- /dev/null +++ b/tests/unit/redundant_copy_in_conversion.cpp @@ -0,0 +1,20 @@ +struct iter { + using iterator_category = int; + int *p; +}; + +struct const_iter { + using iterator_category = int; + const int *p; + const_iter(const iter &o) : p(o.p) {} + const_iter(const const_iter &) = default; +}; + +static void sink(const_iter i) {} + +int main() { + int buf[2] = {0, 0}; + iter it{buf}; + sink(it); + return 0; +} From f602659eed63aa7c6c4a6eb64e86b0c4822d5c79 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 10:17:54 +0100 Subject: [PATCH 06/15] Short-circuit IsRedundantCopyInConversion --- cpp2rust/converter/converter_lib.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index d83371a9..b3f7398f 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -658,6 +658,7 @@ static bool IsIteratorType(clang::QualType qt) { bool IsRedundantCopyInConversion(clang::ASTContext &ctx, const clang::CXXConstructExpr *expr) { + return false; if (auto parents = ctx.getParentMapContext().getParents(*expr); !parents.empty()) { if (auto *parent = parents[0].get()) { From a3e78e5dff525f1f7c17d446651bddf978048316 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 10:34:28 +0100 Subject: [PATCH 07/15] Use std::map::iterator --- .../refcount/redundant_copy_in_conversion.rs | 65 ++++++------------- .../unsafe/redundant_copy_in_conversion.rs | 35 ++-------- tests/unit/redundant_copy_in_conversion.cpp | 23 ++----- 3 files changed, 32 insertions(+), 91 deletions(-) diff --git a/tests/unit/out/refcount/redundant_copy_in_conversion.rs b/tests/unit/out/refcount/redundant_copy_in_conversion.rs index d6d1492f..4feb3b45 100644 --- a/tests/unit/out/refcount/redundant_copy_in_conversion.rs +++ b/tests/unit/out/refcount/redundant_copy_in_conversion.rs @@ -6,54 +6,27 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -#[derive(Default)] -pub struct iter { - pub p: Value>, -} -impl Clone for iter { - fn clone(&self) -> Self { - let mut this = Self { - p: Rc::new(RefCell::new((*self.p.borrow()).clone())), - }; - this - } -} -impl ByteRepr for iter {} -#[derive(Default)] -pub struct const_iter { - pub p: Value>, -} -impl const_iter { - pub fn const_iter(o: Ptr) -> Self { - let mut this = Self { - p: Rc::new(RefCell::new((*(*o.upgrade().deref()).p.borrow()).clone())), - }; - this - } -} -impl Clone for const_iter { - fn clone(&self) -> Self { - let mut this = Self { - p: Rc::new(RefCell::new(Ptr::::null())), - }; - this - } -} -impl ByteRepr for const_iter {} -pub fn sink_0(i: const_iter) { - let i: Value = Rc::new(RefCell::new(i)); -} pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let buf: Value> = Rc::new(RefCell::new(Box::new([0, 0]))); - let it: Value = Rc::new(RefCell::new(iter { - p: Rc::new(RefCell::new((buf.as_pointer() as Ptr))), - })); - ({ - let _i: const_iter = const_iter::const_iter(it.as_pointer()); - sink_0(_i) - }); - return 0; + let m: Value>> = Rc::new(RefCell::new(BTreeMap::new())); + (m.as_pointer() as Ptr>>) + .with_mut(|__v: &mut BTreeMap>| { + __v.entry(0.clone()) + .or_insert_with(|| Rc::new(RefCell::new(::default()))) + .as_pointer() + }) + .write(1); + let end: Value> = Rc::new(RefCell::new(RefcountMapIter::end( + (m.as_pointer() as Ptr>>), + ))); + let const_it: Value> = Rc::new(RefCell::new( + RefcountMapIter::find_key((m.as_pointer() as Ptr>>), &0), + )); + return if (*const_it.borrow()) == (*end.borrow()) { + 0 + } else { + 1 + }; } diff --git a/tests/unit/out/unsafe/redundant_copy_in_conversion.rs b/tests/unit/out/unsafe/redundant_copy_in_conversion.rs index 75fbe3d9..2099e772 100644 --- a/tests/unit/out/unsafe/redundant_copy_in_conversion.rs +++ b/tests/unit/out/unsafe/redundant_copy_in_conversion.rs @@ -6,38 +6,17 @@ 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 iter { - pub p: *mut i32, -} -#[repr(C)] -#[derive(Copy, Clone, Default)] -pub struct const_iter { - pub p: *const i32, -} -impl const_iter { - pub unsafe fn const_iter(o: *const iter) -> Self { - let mut this = Self { - p: (*o).p.cast_const(), - }; - this - } -} -pub unsafe fn sink_0(mut i: const_iter) {} pub fn main() { unsafe { std::process::exit(main_0() as i32); } } unsafe fn main_0() -> i32 { - let mut buf: [i32; 2] = [0, 0]; - let mut it: iter = iter { - p: buf.as_mut_ptr(), - }; - (unsafe { - let _i: const_iter = const_iter::const_iter(&it as *const iter); - sink_0(_i) - }); - return 0; + let mut m: BTreeMap> = BTreeMap::new(); + (*m.entry(0).or_default().as_mut()) = 1; + let mut end: UnsafeMapIterator = + UnsafeMapIterator::end(&m as *const BTreeMap>); + let mut const_it: UnsafeMapIterator = + UnsafeMapIterator::find_key(&m as *const BTreeMap>, &0); + return if const_it == end { 0 } else { 1 }; } diff --git a/tests/unit/redundant_copy_in_conversion.cpp b/tests/unit/redundant_copy_in_conversion.cpp index 9150cacf..ac3439ae 100644 --- a/tests/unit/redundant_copy_in_conversion.cpp +++ b/tests/unit/redundant_copy_in_conversion.cpp @@ -1,20 +1,9 @@ -struct iter { - using iterator_category = int; - int *p; -}; - -struct const_iter { - using iterator_category = int; - const int *p; - const_iter(const iter &o) : p(o.p) {} - const_iter(const const_iter &) = default; -}; - -static void sink(const_iter i) {} +#include int main() { - int buf[2] = {0, 0}; - iter it{buf}; - sink(it); - return 0; + std::map m; + m[0] = 1; + std::map::iterator end = m.end(); + std::map::const_iterator const_it = m.find(0); + return const_it == end ? 0 : 1; } From 8266bc2e43dbe79df2b9e2428c5991221b23e9e5 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 11:11:53 +0100 Subject: [PATCH 08/15] Remove bottom-up IsRedundantCopyInConversion with top-down PushSuppressIteratorClone --- cpp2rust/converter/converter.cpp | 6 ++- cpp2rust/converter/converter.h | 38 +++++++++++++++++++ cpp2rust/converter/converter_lib.cpp | 28 -------------- cpp2rust/converter/converter_lib.h | 3 -- .../converter/models/converter_refcount.cpp | 3 +- 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 37aea766..ea7ce003 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2757,6 +2757,8 @@ void Converter::ConvertCXXConstructExprArgs(clang::CXXConstructExpr *expr) { } bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { + PushSuppressIteratorClone push(*this, expr); + if (auto str = GetMappedAsString(expr, expr->getArgs(), expr->getNumArgs()); !str.empty()) { StrCat(str); @@ -2767,8 +2769,10 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { if (ctor->isCopyOrMoveConstructor() || (ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 && ctor->getParamDecl(0)->getType()->isRValueReferenceType())) { + // Take supress before recursing into the child. + bool suppress = PushSuppressIteratorClone::take(*this, expr); Convert(expr->getArg(0)); - if (ctor->isCopyConstructor() && !IsRedundantCopyInConversion(ctx_, expr)) { + if (ctor->isCopyConstructor() && !suppress) { StrCat(".clone()"); } return false; diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index a98bbf40..bb9b3525 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -529,6 +529,7 @@ class Converter : public clang::RecursiveASTVisitor { bool in_function_formals_ = false; bool in_const_initializer_ = false; std::optional autoref_mut_; + bool suppress_iterator_clone_ = false; struct PushExplicitAutoref { Converter &c; @@ -540,6 +541,43 @@ class Converter : public clang::RecursiveASTVisitor { ~PushExplicitAutoref() { c.autoref_mut_ = prev; } }; + struct PushSuppressIteratorClone { + Converter &c; + bool prev; + PushSuppressIteratorClone(Converter &c, clang::CXXConstructExpr *expr) + : c(c), prev(c.suppress_iterator_clone_) { + auto *ctor = expr->getConstructor(); + if (ctor->isConvertingConstructor(/*AllowExplicit=*/false) && + ctor->getNumParams() == 1 && IsIteratorType(expr->getType())) { + c.suppress_iterator_clone_ = true; + } + } + ~PushSuppressIteratorClone() { c.suppress_iterator_clone_ = prev; } + PushSuppressIteratorClone(const PushSuppressIteratorClone &) = delete; + PushSuppressIteratorClone & + operator=(const PushSuppressIteratorClone &) = delete; + + static bool take(Converter &c, clang::CXXConstructExpr *inner) { + bool suppress = + c.suppress_iterator_clone_ && IsIteratorType(inner->getType()); + c.suppress_iterator_clone_ = false; + return suppress; + } + + private: + static bool IsIteratorType(clang::QualType qt) { + if (auto *record = qt->getAsCXXRecordDecl()) { + for (auto *d : record->decls()) { + if (auto *tnd = llvm::dyn_cast(d)) { + if (tnd->getName() == "iterator_category") + return true; + } + } + } + return false; + } + }; + struct PushConstInitializer { Converter &c; bool prev; diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index b3f7398f..4f60cf64 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -644,34 +644,6 @@ std::string GetClassName(clang::QualType type) { return {}; } -static bool IsIteratorType(clang::QualType qt) { - if (auto *record = qt->getAsCXXRecordDecl()) { - for (auto *d : record->decls()) { - if (auto *tnd = llvm::dyn_cast(d)) { - if (tnd->getName() == "iterator_category") - return true; - } - } - } - return false; -} - -bool IsRedundantCopyInConversion(clang::ASTContext &ctx, - const clang::CXXConstructExpr *expr) { - return false; - if (auto parents = ctx.getParentMapContext().getParents(*expr); - !parents.empty()) { - if (auto *parent = parents[0].get()) { - if (parent->getConstructor()->isConvertingConstructor( - /* AllowExplicit= */ false)) { - return IsIteratorType(expr->getType()) && - IsIteratorType(parent->getType()); - } - } - } - return false; -} - bool IsVaListType(clang::QualType type) { for (auto t = type; !t.isNull();) { if (auto *adjusted = t->getAs()) { diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index d9c97622..aa5d0c2b 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -145,9 +145,6 @@ GetForRangeIteratorType(const clang::CXXForRangeStmt *for_range); std::string GetClassName(clang::QualType type); -bool IsRedundantCopyInConversion(clang::ASTContext &ctx, - const clang::CXXConstructExpr *expr); - bool IsVaListType(clang::QualType type); bool IsBuiltinVaStart(const clang::CallExpr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index fc1e0d27..75a1661e 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1541,6 +1541,7 @@ std::string ConverterRefCount::ConvertStream(clang::Expr *expr) { bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { PushConversionKind push(*this, ConversionKind::Unboxed); + PushSuppressIteratorClone push_suppress(*this, expr); if (auto str = GetMappedAsString(expr, expr->getArgs(), expr->getNumArgs()); !str.empty()) { @@ -1564,7 +1565,7 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { } if (ctor->isCopyConstructor()) { - StrCat(IsRedundantCopyInConversion(ctx_, expr) + StrCat(PushSuppressIteratorClone::take(*this, expr) ? ConvertRValue(expr->getArg(0)) : ConvertFreshRValue(expr->getArg(0))); return false; From a5cd0d0ee340faa47391f04331676356e04cbdfa Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 11:12:02 +0100 Subject: [PATCH 09/15] Update tests --- tests/unit/out/refcount/map.rs | 2 +- tests/unit/out/unsafe/map.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/out/refcount/map.rs b/tests/unit/out/refcount/map.rs index c48dad05..7af8fb5f 100644 --- a/tests/unit/out/refcount/map.rs +++ b/tests/unit/out/refcount/map.rs @@ -274,7 +274,7 @@ fn main_0() -> i32 { ); RefcountMapIter::erase( ((r).clone() as Ptr>>), - &(*it4.borrow()).clone(), + &(*it4.borrow()), ); assert!(((*r.upgrade().deref()).len() as u64 == 3_u64)); assert!( diff --git a/tests/unit/out/unsafe/map.rs b/tests/unit/out/unsafe/map.rs index 88b42d14..c3a14f46 100644 --- a/tests/unit/out/unsafe/map.rs +++ b/tests/unit/out/unsafe/map.rs @@ -91,7 +91,7 @@ unsafe fn main_0() -> i32 { UnsafeMapIterator::find_key(&m as *const BTreeMap>, &4_i16) != UnsafeMapIterator::end(&m as *const BTreeMap>) ); - UnsafeMapIterator::erase(&(*r) as *const BTreeMap>, &it4.clone()); + UnsafeMapIterator::erase(&(*r) as *const BTreeMap>, &it4); assert!((((*r).len() as u64) == (3_u64))); assert!( UnsafeMapIterator::find_key(&m as *const BTreeMap>, &4_i16) From e313d8f6c077833919d514a4bcf1a53268299ca9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 11:20:48 +0100 Subject: [PATCH 10/15] Add comment to explain test --- tests/unit/redundant_copy_in_conversion.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/redundant_copy_in_conversion.cpp b/tests/unit/redundant_copy_in_conversion.cpp index ac3439ae..abfa4984 100644 --- a/tests/unit/redundant_copy_in_conversion.cpp +++ b/tests/unit/redundant_copy_in_conversion.cpp @@ -5,5 +5,13 @@ int main() { m[0] = 1; std::map::iterator end = m.end(); std::map::const_iterator const_it = m.find(0); + // Comparing const_iterator with iterator forces an implicit conversion of + // `end` to const_iterator. The AST shape differs between platforms: + // + // Linux: const_it == ConvertingCtor(end) + // macOS: const_it == ConvertingCtor(CopyCtor(end)) + // + // The extra inner CopyCtor on macOS would emit a redundant .clone() in the + // generated Rust. cpp2rust suppresses it so the output matches Linux. return const_it == end ? 0 : 1; } From 32c8733e9aecd9f82b36046bd8972cbf132ee184 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 11:40:09 +0100 Subject: [PATCH 11/15] Drop the IsIteratorType check --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/converter.h | 26 +++++-------------- .../converter/models/converter_refcount.cpp | 2 +- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index ea7ce003..6ae2bd89 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2770,7 +2770,7 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { (ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 && ctor->getParamDecl(0)->getType()->isRValueReferenceType())) { // Take supress before recursing into the child. - bool suppress = PushSuppressIteratorClone::take(*this, expr); + bool suppress = PushSuppressIteratorClone::take(*this); Convert(expr->getArg(0)); if (ctor->isCopyConstructor() && !suppress) { StrCat(".clone()"); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index bb9b3525..e8adba39 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "converter/lex.h" @@ -547,8 +548,9 @@ class Converter : public clang::RecursiveASTVisitor { PushSuppressIteratorClone(Converter &c, clang::CXXConstructExpr *expr) : c(c), prev(c.suppress_iterator_clone_) { auto *ctor = expr->getConstructor(); - if (ctor->isConvertingConstructor(/*AllowExplicit=*/false) && - ctor->getNumParams() == 1 && IsIteratorType(expr->getType())) { + if (!ctor->isCopyOrMoveConstructor() && + ctor->isConvertingConstructor(/*AllowExplicit=*/false) && + ctor->getNumParams() == 1) { c.suppress_iterator_clone_ = true; } } @@ -557,24 +559,8 @@ class Converter : public clang::RecursiveASTVisitor { PushSuppressIteratorClone & operator=(const PushSuppressIteratorClone &) = delete; - static bool take(Converter &c, clang::CXXConstructExpr *inner) { - bool suppress = - c.suppress_iterator_clone_ && IsIteratorType(inner->getType()); - c.suppress_iterator_clone_ = false; - return suppress; - } - - private: - static bool IsIteratorType(clang::QualType qt) { - if (auto *record = qt->getAsCXXRecordDecl()) { - for (auto *d : record->decls()) { - if (auto *tnd = llvm::dyn_cast(d)) { - if (tnd->getName() == "iterator_category") - return true; - } - } - } - return false; + static bool take(Converter &c) { + return std::exchange(c.suppress_iterator_clone_, false); } }; diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 75a1661e..c194e796 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1565,7 +1565,7 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { } if (ctor->isCopyConstructor()) { - StrCat(PushSuppressIteratorClone::take(*this, expr) + StrCat(PushSuppressIteratorClone::take(*this) ? ConvertRValue(expr->getArg(0)) : ConvertFreshRValue(expr->getArg(0))); return false; From 3f44e5bf27f445eebd60fa781d2ed39564637cf1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 11:51:59 +0100 Subject: [PATCH 12/15] Update tests --- tests/unit/out/refcount/map.rs | 2 +- tests/unit/out/unsafe/map.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/out/refcount/map.rs b/tests/unit/out/refcount/map.rs index 7af8fb5f..c48dad05 100644 --- a/tests/unit/out/refcount/map.rs +++ b/tests/unit/out/refcount/map.rs @@ -274,7 +274,7 @@ fn main_0() -> i32 { ); RefcountMapIter::erase( ((r).clone() as Ptr>>), - &(*it4.borrow()), + &(*it4.borrow()).clone(), ); assert!(((*r.upgrade().deref()).len() as u64 == 3_u64)); assert!( diff --git a/tests/unit/out/unsafe/map.rs b/tests/unit/out/unsafe/map.rs index c3a14f46..88b42d14 100644 --- a/tests/unit/out/unsafe/map.rs +++ b/tests/unit/out/unsafe/map.rs @@ -91,7 +91,7 @@ unsafe fn main_0() -> i32 { UnsafeMapIterator::find_key(&m as *const BTreeMap>, &4_i16) != UnsafeMapIterator::end(&m as *const BTreeMap>) ); - UnsafeMapIterator::erase(&(*r) as *const BTreeMap>, &it4); + UnsafeMapIterator::erase(&(*r) as *const BTreeMap>, &it4.clone()); assert!((((*r).len() as u64) == (3_u64))); assert!( UnsafeMapIterator::find_key(&m as *const BTreeMap>, &4_i16) From f2b1e1a6e10865014b3ce9e67eaf2b350a0b78b9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 14:13:45 +0100 Subject: [PATCH 13/15] Clone iterator --- rules/map/ir_refcount.json | 21 +++++++++++++++++---- rules/map/ir_unsafe.json | 21 +++++++++++++++++---- rules/map/tgt_refcount.rs | 4 ++-- rules/map/tgt_unsafe.rs | 4 ++-- tests/unit/out/refcount/map.rs | 17 ++++++++++------- tests/unit/out/unsafe/map.rs | 15 ++++++++------- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/rules/map/ir_refcount.json b/rules/map/ir_refcount.json index 799bd5ca..5730c1fb 100644 --- a/rules/map/ir_refcount.json +++ b/rules/map/ir_refcount.json @@ -462,14 +462,27 @@ "f19": { "body": [ { - "placeholder": { - "arg": 0, - "access": "read" + "method_call": { + "receiver": [ + { + "placeholder": { + "arg": 0, + "access": "read" + } + } + ], + "body": [ + { + "text": ".clone()" + } + ] } } ], "generics": { - "T1": [], + "T1": [ + "Clone" + ], "T2": [] }, "params": { diff --git a/rules/map/ir_unsafe.json b/rules/map/ir_unsafe.json index a876e246..a8f9aeaf 100644 --- a/rules/map/ir_unsafe.json +++ b/rules/map/ir_unsafe.json @@ -466,14 +466,27 @@ "f19": { "body": [ { - "placeholder": { - "arg": 0, - "access": "read" + "method_call": { + "receiver": [ + { + "placeholder": { + "arg": 0, + "access": "read" + } + } + ], + "body": [ + { + "text": ".clone()" + } + ] } } ], "generics": { - "T1": [], + "T1": [ + "Clone" + ], "T2": [] }, "params": { diff --git a/rules/map/tgt_refcount.rs b/rules/map/tgt_refcount.rs index bff90712..ad950c5c 100644 --- a/rules/map/tgt_refcount.rs +++ b/rules/map/tgt_refcount.rs @@ -120,8 +120,8 @@ fn f17( RefcountMapIter::find_key(a0, &a1) } -fn f19(a0: RefcountMapIter) -> RefcountMapIter { - a0 +fn f19(a0: RefcountMapIter) -> RefcountMapIter { + a0.clone() } fn f20(a0: RefcountMapIter) -> Value { diff --git a/rules/map/tgt_unsafe.rs b/rules/map/tgt_unsafe.rs index a26a0be6..6fd8733a 100644 --- a/rules/map/tgt_unsafe.rs +++ b/rules/map/tgt_unsafe.rs @@ -83,8 +83,8 @@ unsafe fn f17(a0: BTreeMap>, a1: T1) -> UnsafeM UnsafeMapIterator::find_key(&a0 as *const BTreeMap>, &a1) } -unsafe fn f19(a0: UnsafeMapIterator) -> UnsafeMapIterator { - a0 +unsafe fn f19(a0: UnsafeMapIterator) -> UnsafeMapIterator { + a0.clone() } unsafe fn f20(a0: UnsafeMapIterator) -> *const T1 { diff --git a/tests/unit/out/refcount/map.rs b/tests/unit/out/refcount/map.rs index c48dad05..ef8db5fe 100644 --- a/tests/unit/out/refcount/map.rs +++ b/tests/unit/out/refcount/map.rs @@ -192,7 +192,8 @@ fn main_0() -> i32 { &1_i16, ))); let const_it: Value> = Rc::new(RefCell::new( - RefcountMapIter::find_key((m.as_pointer() as Ptr>>), &10_i16), + RefcountMapIter::find_key((m.as_pointer() as Ptr>>), &10_i16) + .clone(), )); let x1: Value = Rc::new(RefCell::new(if (*it.borrow()) == (*end.borrow()) { 0_u32 @@ -200,11 +201,13 @@ fn main_0() -> i32 { (*(*it.borrow()).second().borrow()) })); assert!(((*x1.borrow()) == 4_u32)); - let x2: Value = Rc::new(RefCell::new(if (*const_it.borrow()) == (*end.borrow()) { - 0_u32 - } else { - (*(*const_it.borrow()).second().borrow()) - })); + let x2: Value = Rc::new(RefCell::new( + if (*const_it.borrow()) == (*end.borrow()).clone() { + 0_u32 + } else { + (*(*const_it.borrow()).second().borrow()) + }, + )); assert!(((*x2.borrow()) == 0_u32)); let x3: Value = Rc::new(RefCell::new( if (*it.borrow()) @@ -218,7 +221,7 @@ fn main_0() -> i32 { assert!(((*x3.borrow()) == 4_u32)); let x4: Value = Rc::new(RefCell::new( if (*const_it.borrow()) - == RefcountMapIter::end((m.as_pointer() as Ptr>>)) + == RefcountMapIter::end((m.as_pointer() as Ptr>>)).clone() { 0_u32 } else { diff --git a/tests/unit/out/unsafe/map.rs b/tests/unit/out/unsafe/map.rs index 88b42d14..a20b2ebe 100644 --- a/tests/unit/out/unsafe/map.rs +++ b/tests/unit/out/unsafe/map.rs @@ -50,10 +50,10 @@ unsafe fn main_0() -> i32 { let mut it: UnsafeMapIterator = UnsafeMapIterator::find_key(&m as *const BTreeMap>, &1_i16); let mut const_it: UnsafeMapIterator = - UnsafeMapIterator::find_key(&m as *const BTreeMap>, &10_i16); + UnsafeMapIterator::find_key(&m as *const BTreeMap>, &10_i16).clone(); let mut x1: u32 = if it == end { 0_u32 } else { *it.second() }; assert!(((x1) == (4_u32))); - let mut x2: u32 = if const_it == end { + let mut x2: u32 = if const_it == end.clone() { 0_u32 } else { *const_it.second() @@ -65,11 +65,12 @@ unsafe fn main_0() -> i32 { *it.second() }; assert!(((x3) == (4_u32))); - let mut x4: u32 = if const_it == UnsafeMapIterator::end(&m as *const BTreeMap>) { - 0_u32 - } else { - *const_it.second() - }; + let mut x4: u32 = + if const_it == UnsafeMapIterator::end(&m as *const BTreeMap>).clone() { + 0_u32 + } else { + *const_it.second() + }; assert!(((x4) == (0_u32))); (*m.entry(4_i16).or_default().as_mut()) = 5_u32; let mut it4: UnsafeMapIterator = From 1bca3ec954625fe25e490a2ec3d3a84165ae108b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 14:14:48 +0100 Subject: [PATCH 14/15] Check if test passes on macOS --- .../refcount/redundant_copy_in_conversion.rs | 29 +++++++++++++++++-- .../unsafe/redundant_copy_in_conversion.rs | 15 ++++++++-- tests/unit/redundant_copy_in_conversion.cpp | 13 +++++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/tests/unit/out/refcount/redundant_copy_in_conversion.rs b/tests/unit/out/refcount/redundant_copy_in_conversion.rs index 4feb3b45..d1fd38e1 100644 --- a/tests/unit/out/refcount/redundant_copy_in_conversion.rs +++ b/tests/unit/out/refcount/redundant_copy_in_conversion.rs @@ -6,6 +6,15 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; +pub fn sink_0(it: RefcountMapIter) -> i32 { + let it: Value> = Rc::new(RefCell::new(it)); + let cit: Value> = Rc::new(RefCell::new((*it.borrow()).clone())); + return if (*cit.borrow()) == (*it.borrow()).clone() { + (*(*it.borrow()).second().borrow()) + } else { + 0 + }; +} pub fn main() { std::process::exit(main_0()); } @@ -21,12 +30,26 @@ fn main_0() -> i32 { let end: Value> = Rc::new(RefCell::new(RefcountMapIter::end( (m.as_pointer() as Ptr>>), ))); - let const_it: Value> = Rc::new(RefCell::new( - RefcountMapIter::find_key((m.as_pointer() as Ptr>>), &0), + let it0: Value> = Rc::new(RefCell::new(RefcountMapIter::find_key( + (m.as_pointer() as Ptr>>), + &0, + ))); + let const_it: Value> = Rc::new(RefCell::new((*it0.borrow()).clone())); + let r: Value = Rc::new(RefCell::new( + if (*const_it.borrow()) == (*end.borrow()).clone() { + 0 + } else { + 1 + }, )); - return if (*const_it.borrow()) == (*end.borrow()) { + (*r.borrow_mut()) += ({ + let _it: RefcountMapIter = (*it0.borrow()).clone(); + sink_0(_it) + }); + (*r.borrow_mut()) += if (*end.borrow()) == (*end.borrow()) { 0 } else { 1 }; + return (*r.borrow()); } diff --git a/tests/unit/out/unsafe/redundant_copy_in_conversion.rs b/tests/unit/out/unsafe/redundant_copy_in_conversion.rs index 2099e772..d78561fc 100644 --- a/tests/unit/out/unsafe/redundant_copy_in_conversion.rs +++ b/tests/unit/out/unsafe/redundant_copy_in_conversion.rs @@ -6,6 +6,10 @@ use std::collections::BTreeMap; use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; +pub unsafe fn sink_0(mut it: UnsafeMapIterator) -> i32 { + let mut cit: UnsafeMapIterator = it.clone(); + return if cit == it.clone() { *it.second() } else { 0 }; +} pub fn main() { unsafe { std::process::exit(main_0() as i32); @@ -16,7 +20,14 @@ unsafe fn main_0() -> i32 { (*m.entry(0).or_default().as_mut()) = 1; let mut end: UnsafeMapIterator = UnsafeMapIterator::end(&m as *const BTreeMap>); - let mut const_it: UnsafeMapIterator = + let mut it0: UnsafeMapIterator = UnsafeMapIterator::find_key(&m as *const BTreeMap>, &0); - return if const_it == end { 0 } else { 1 }; + let mut const_it: UnsafeMapIterator = it0.clone(); + let mut r: i32 = if const_it == end.clone() { 0 } else { 1 }; + r += (unsafe { + let _it: UnsafeMapIterator = it0.clone(); + sink_0(_it) + }); + r += if end == end { 0 } else { 1 }; + return r; } diff --git a/tests/unit/redundant_copy_in_conversion.cpp b/tests/unit/redundant_copy_in_conversion.cpp index abfa4984..cbea23b1 100644 --- a/tests/unit/redundant_copy_in_conversion.cpp +++ b/tests/unit/redundant_copy_in_conversion.cpp @@ -1,10 +1,16 @@ #include +static int sink(std::map::iterator it) { + std::map::const_iterator cit(it); + return cit == it ? it->second : 0; +} + int main() { std::map m; m[0] = 1; std::map::iterator end = m.end(); - std::map::const_iterator const_it = m.find(0); + std::map::iterator it0 = m.find(0); + std::map::const_iterator const_it = it0; // Comparing const_iterator with iterator forces an implicit conversion of // `end` to const_iterator. The AST shape differs between platforms: // @@ -13,5 +19,8 @@ int main() { // // The extra inner CopyCtor on macOS would emit a redundant .clone() in the // generated Rust. cpp2rust suppresses it so the output matches Linux. - return const_it == end ? 0 : 1; + int r = const_it == end ? 0 : 1; + r += sink(it0); + r += end == end ? 0 : 1; + return r; } From 67a15b2057c5f793be49a1e42b3f75de736c2c2e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 18 May 2026 14:35:01 +0100 Subject: [PATCH 15/15] Re-add IsIteratorType --- cpp2rust/converter/converter.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index e8adba39..fa81c6a2 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -550,7 +550,7 @@ class Converter : public clang::RecursiveASTVisitor { auto *ctor = expr->getConstructor(); if (!ctor->isCopyOrMoveConstructor() && ctor->isConvertingConstructor(/*AllowExplicit=*/false) && - ctor->getNumParams() == 1) { + ctor->getNumParams() == 1 && IsIteratorType(expr->getType())) { c.suppress_iterator_clone_ = true; } } @@ -562,6 +562,19 @@ class Converter : public clang::RecursiveASTVisitor { static bool take(Converter &c) { return std::exchange(c.suppress_iterator_clone_, false); } + + private: + static bool IsIteratorType(clang::QualType qt) { + if (auto *record = qt->getAsCXXRecordDecl()) { + for (auto *d : record->decls()) { + if (auto *tnd = llvm::dyn_cast(d)) { + if (tnd->getName() == "iterator_category") + return true; + } + } + } + return false; + } }; struct PushConstInitializer {