Skip to content

Commit 08a11c2

Browse files
authored
Suppress clone in iterator conversion (#112)
From, tests/unit/redundant_copy_in_conversion: ```cpp // 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. const_it == end ? 0 : 1; ``` This only happens on iterator types because the distinction between iterator and const_iterator. Other containers don't have this issue.
1 parent 919d1ab commit 08a11c2

17 files changed

Lines changed: 288 additions & 42 deletions

cpp2rust/converter/converter.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2754,6 +2754,8 @@ void Converter::ConvertCXXConstructExprArgs(clang::CXXConstructExpr *expr) {
27542754
}
27552755

27562756
bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
2757+
PushSuppressIteratorClone push(*this, expr);
2758+
27572759
if (auto str = GetMappedAsString(expr, expr->getArgs(), expr->getNumArgs());
27582760
!str.empty()) {
27592761
StrCat(str);
@@ -2764,8 +2766,10 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
27642766
if (ctor->isCopyOrMoveConstructor() ||
27652767
(ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 &&
27662768
ctor->getParamDecl(0)->getType()->isRValueReferenceType())) {
2769+
// Take supress before recursing into the child.
2770+
bool suppress = PushSuppressIteratorClone::take(*this);
27672771
Convert(expr->getArg(0));
2768-
if (ctor->isCopyConstructor() && !IsRedundantCopyInConversion(ctx_, expr)) {
2772+
if (ctor->isCopyConstructor() && !suppress) {
27692773
StrCat(".clone()");
27702774
}
27712775
return false;

cpp2rust/converter/converter.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <string>
1414
#include <unordered_map>
1515
#include <unordered_set>
16+
#include <utility>
1617
#include <vector>
1718

1819
#include "converter/lex.h"
@@ -529,6 +530,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
529530
bool in_function_formals_ = false;
530531
bool in_const_initializer_ = false;
531532
std::optional<bool> autoref_mut_;
533+
bool suppress_iterator_clone_ = false;
532534

533535
struct PushExplicitAutoref {
534536
Converter &c;
@@ -540,6 +542,41 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
540542
~PushExplicitAutoref() { c.autoref_mut_ = prev; }
541543
};
542544

545+
struct PushSuppressIteratorClone {
546+
Converter &c;
547+
bool prev;
548+
PushSuppressIteratorClone(Converter &c, clang::CXXConstructExpr *expr)
549+
: c(c), prev(c.suppress_iterator_clone_) {
550+
auto *ctor = expr->getConstructor();
551+
if (!ctor->isCopyOrMoveConstructor() &&
552+
ctor->isConvertingConstructor(/*AllowExplicit=*/false) &&
553+
ctor->getNumParams() == 1 && IsIteratorType(expr->getType())) {
554+
c.suppress_iterator_clone_ = true;
555+
}
556+
}
557+
~PushSuppressIteratorClone() { c.suppress_iterator_clone_ = prev; }
558+
PushSuppressIteratorClone(const PushSuppressIteratorClone &) = delete;
559+
PushSuppressIteratorClone &
560+
operator=(const PushSuppressIteratorClone &) = delete;
561+
562+
static bool take(Converter &c) {
563+
return std::exchange(c.suppress_iterator_clone_, false);
564+
}
565+
566+
private:
567+
static bool IsIteratorType(clang::QualType qt) {
568+
if (auto *record = qt->getAsCXXRecordDecl()) {
569+
for (auto *d : record->decls()) {
570+
if (auto *tnd = llvm::dyn_cast<clang::TypedefNameDecl>(d)) {
571+
if (tnd->getName() == "iterator_category")
572+
return true;
573+
}
574+
}
575+
}
576+
return false;
577+
}
578+
};
579+
543580
struct PushConstInitializer {
544581
Converter &c;
545582
bool prev;

cpp2rust/converter/converter_lib.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -644,16 +644,6 @@ std::string GetClassName(clang::QualType type) {
644644
return {};
645645
}
646646

647-
bool IsRedundantCopyInConversion(clang::ASTContext &ctx,
648-
const clang::CXXConstructExpr *expr) {
649-
auto parents = ctx.getParentMapContext().getParents(*expr);
650-
if (parents.empty()) {
651-
return false;
652-
}
653-
auto *parent = parents[0].get<clang::CXXConstructExpr>();
654-
return parent && parent->getConstructor()->isConvertingConstructor(false);
655-
}
656-
657647
bool IsVaListType(clang::QualType type) {
658648
for (auto t = type; !t.isNull();) {
659649
if (auto *adjusted = t->getAs<clang::AdjustedType>()) {

cpp2rust/converter/converter_lib.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,6 @@ GetForRangeIteratorType(const clang::CXXForRangeStmt *for_range);
145145

146146
std::string GetClassName(clang::QualType type);
147147

148-
bool IsRedundantCopyInConversion(clang::ASTContext &ctx,
149-
const clang::CXXConstructExpr *expr);
150-
151148
bool IsVaListType(clang::QualType type);
152149

153150
bool IsBuiltinVaStart(const clang::CallExpr *expr);

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,7 @@ std::string ConverterRefCount::ConvertStream(clang::Expr *expr) {
15411541

15421542
bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
15431543
PushConversionKind push(*this, ConversionKind::Unboxed);
1544+
PushSuppressIteratorClone push_suppress(*this, expr);
15441545

15451546
if (auto str = GetMappedAsString(expr, expr->getArgs(), expr->getNumArgs());
15461547
!str.empty()) {
@@ -1564,7 +1565,9 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
15641565
}
15651566

15661567
if (ctor->isCopyConstructor()) {
1567-
StrCat(ConvertRValue(expr->getArg(0)));
1568+
StrCat(PushSuppressIteratorClone::take(*this)
1569+
? ConvertRValue(expr->getArg(0))
1570+
: ConvertFreshRValue(expr->getArg(0)));
15681571
return false;
15691572
}
15701573

rules/map/ir_refcount.json

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,14 +462,27 @@
462462
"f19": {
463463
"body": [
464464
{
465-
"placeholder": {
466-
"arg": 0,
467-
"access": "read"
465+
"method_call": {
466+
"receiver": [
467+
{
468+
"placeholder": {
469+
"arg": 0,
470+
"access": "read"
471+
}
472+
}
473+
],
474+
"body": [
475+
{
476+
"text": ".clone()"
477+
}
478+
]
468479
}
469480
}
470481
],
471482
"generics": {
472-
"T1": [],
483+
"T1": [
484+
"Clone"
485+
],
473486
"T2": []
474487
},
475488
"params": {

rules/map/ir_unsafe.json

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,14 +466,27 @@
466466
"f19": {
467467
"body": [
468468
{
469-
"placeholder": {
470-
"arg": 0,
471-
"access": "read"
469+
"method_call": {
470+
"receiver": [
471+
{
472+
"placeholder": {
473+
"arg": 0,
474+
"access": "read"
475+
}
476+
}
477+
],
478+
"body": [
479+
{
480+
"text": ".clone()"
481+
}
482+
]
472483
}
473484
}
474485
],
475486
"generics": {
476-
"T1": [],
487+
"T1": [
488+
"Clone"
489+
],
477490
"T2": []
478491
},
479492
"params": {

rules/map/tgt_refcount.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ fn f17<T1: Ord + Clone + 'static, T2: 'static>(
120120
RefcountMapIter::find_key(a0, &a1)
121121
}
122122

123-
fn f19<T1, T2>(a0: RefcountMapIter<T1, T2>) -> RefcountMapIter<T1, T2> {
124-
a0
123+
fn f19<T1: Clone, T2>(a0: RefcountMapIter<T1, T2>) -> RefcountMapIter<T1, T2> {
124+
a0.clone()
125125
}
126126

127127
fn f20<T1: Ord + Clone + 'static, T2: 'static>(a0: RefcountMapIter<T1, T2>) -> Value<T1> {

rules/map/tgt_unsafe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ unsafe fn f17<T1: Ord + Clone, T2>(a0: BTreeMap<T1, Box<T2>>, a1: T1) -> UnsafeM
8383
UnsafeMapIterator::find_key(&a0 as *const BTreeMap<T1, Box<T2>>, &a1)
8484
}
8585

86-
unsafe fn f19<T1, T2>(a0: UnsafeMapIterator<T1, T2>) -> UnsafeMapIterator<T1, T2> {
87-
a0
86+
unsafe fn f19<T1: Clone, T2>(a0: UnsafeMapIterator<T1, T2>) -> UnsafeMapIterator<T1, T2> {
87+
a0.clone()
8888
}
8989

9090
unsafe fn f20<T1: Ord + Clone, T2>(a0: UnsafeMapIterator<T1, T2>) -> *const T1 {

tests/unit/initializer_list.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <cassert>
2+
#include <cstddef>
3+
#include <initializer_list>
4+
#include <vector>
5+
6+
size_t f(std::initializer_list<int> bytes) {
7+
std::vector<int> *buf = new std::vector<int>(bytes);
8+
size_t n = bytes.size();
9+
delete buf;
10+
return n;
11+
}
12+
13+
int main() {
14+
assert(f({1, 2, 3}) == 3);
15+
return 0;
16+
}

0 commit comments

Comments
 (0)