Skip to content

Commit 0050fcf

Browse files
authored
Translate move constructor and move assignment (#343)
Move constructor is translated as a normal constructor: ```rs impl MoveOnly { pub unsafe fn MoveOnly_pmutMoveOnly(o: *mut MoveOnly) -> Self { let mut this = Self { v: (*o).v }; (*o).v = 0; this } } ``` Move assignment is translated using `operator_assign`: ```rs impl MoveOnly { pub unsafe fn operator_assign_pmutMoveOnly(&mut self, o: *mut MoveOnly) -> *mut MoveOnly { if ((self as *mut MoveOnly) == (o)) { return &mut (*(self as *mut MoveOnly)) as *mut MoveOnly; } self.v = (*o).v; (*o).v = 0; return &mut (*(self as *mut MoveOnly)) as *mut MoveOnly; } } ``` The combination defaulted move constructor and non-default copy constructor is not supported yet. `std::move` is translated as a clone() currently. It assumes that a clone is bitwise, i.e. the copy constructor is implicit. If it's not, the `std::move` would call the user defined copy constructor which is wrong. I will work on this in a future PR.
1 parent ed59f87 commit 0050fcf

26 files changed

Lines changed: 2059 additions & 57 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <clang/Basic/SourceManager.h>
1010
#include <llvm/ADT/DenseMap.h>
1111
#include <llvm/Support/ConvertUTF.h>
12+
#include <llvm/Support/ErrorHandling.h>
1213

1314
#include <algorithm>
1415
#include <format>
@@ -624,28 +625,6 @@ void Converter::EmitScopedDestructor(const clang::VarDecl *decl) {
624625
kDestructorName));
625626
}
626627

627-
static bool hasUserDefinedNonDefaultMoveCtor(clang::CXXRecordDecl *decl) {
628-
for (const auto *ctor : decl->ctors()) {
629-
if (ctor->isMoveConstructor()) {
630-
auto source = ctor->getDefinition() ? ctor->getDefinition() : ctor;
631-
if (source->isUserProvided() && !source->isDefaulted()) {
632-
return true;
633-
}
634-
}
635-
}
636-
637-
for (const auto *method : decl->methods()) {
638-
if (method->isMoveAssignmentOperator()) {
639-
auto source = method->getDefinition() ? method->getDefinition() : method;
640-
if (source->isUserProvided() && !source->isDefaulted()) {
641-
return true;
642-
}
643-
}
644-
}
645-
646-
return false;
647-
}
648-
649628
bool IsPointerType(clang::QualType qual_type) {
650629
return qual_type->isPointerType() ||
651630
(qual_type->isArrayType() &&
@@ -954,10 +933,6 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
954933
return false;
955934
}
956935

957-
if (hasUserDefinedNonDefaultMoveCtor(decl)) {
958-
assert(0 && "unsupported user-defined move ctor");
959-
}
960-
961936
sema_->ForceDeclarationOfImplicitMembers(decl);
962937
for (auto ctor : decl->ctors()) {
963938
if (ctor->isCopyConstructor() && ctor->isImplicit() &&
@@ -1057,7 +1032,7 @@ std::string Converter::GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl) {
10571032
}
10581033

10591034
std::string Converter::GetCtorName(clang::CXXConstructorDecl *decl) {
1060-
if (decl->isCopyConstructor()) {
1035+
if (decl->isCopyOrMoveConstructor()) {
10611036
return GetOverloadedFunctionName(decl);
10621037
}
10631038
return GetRecordName(decl->getParent()) +
@@ -1072,10 +1047,8 @@ bool Converter::VisitCXXConstructorDecl(clang::CXXConstructorDecl *decl) {
10721047
}
10731048
PushCurrFunction push_fn(*this, decl);
10741049

1075-
if (decl->isMoveConstructor()) {
1076-
assert(0 && "user-defined move constructor are not supported");
1077-
}
1078-
if (decl->isCopyConstructor() && !decl->doesThisDeclarationHaveABody()) {
1050+
if (decl->isCopyOrMoveConstructor() &&
1051+
!decl->doesThisDeclarationHaveABody()) {
10791052
return false;
10801053
}
10811054

@@ -1740,7 +1713,6 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
17401713
return false;
17411714
}
17421715
StrCat(std::format("{}", ToString(expr->getArg(0))));
1743-
computed_expr_type_ = ComputedExprType::FreshValue;
17441716
return false;
17451717
}
17461718

@@ -3372,12 +3344,22 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
33723344
}
33733345

33743346
auto *ctor = expr->getConstructor();
3347+
// Default move is translated using a bitwise .clone() implementation.
3348+
// Bitwise clone is only satisfied by default copy constructor. If the copy
3349+
// constructor is user defined, then default move calls copy constructor,
3350+
// which is wrong.
3351+
if (IsDefaultedMoveConstructor(ctor) &&
3352+
!HasDefaultedCopyConstructor(ctor->getParent())) {
3353+
llvm::report_fatal_error("defaulted move constructor without a fieldwise "
3354+
"copy constructor is not supported");
3355+
}
3356+
33753357
if (IsPassThroughConstructor(ctor)) {
33763358
// Take suppress before recursing into the child.
33773359
bool suppress = PushSuppressIteratorClone::take(*this);
33783360
Convert(expr->getArg(0));
3379-
if (ctor->isCopyConstructor() && !suppress &&
3380-
!TypeIsCopyable(expr->getType())) {
3361+
if ((ctor->isCopyConstructor() || IsDefaultedMoveConstructor(ctor)) &&
3362+
!suppress && !TypeIsCopyable(expr->getType())) {
33813363
StrCat(".clone()");
33823364
}
33833365
return false;
@@ -3843,12 +3825,11 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) {
38433825

38443826
std::vector<const char *> struct_attrs;
38453827

3846-
bool derive_clone =
3847-
IsCopyConstructible(decl) && !HasUserDefinedCopyConstructor(decl);
3848-
if (derive_clone && RecordHasCopyableFields(decl)) {
3828+
if (HasDefaultedCopyConstructor(decl) && RecordHasCopyableFields(decl)) {
38493829
struct_attrs.emplace_back("Copy");
38503830
}
3851-
if (derive_clone) {
3831+
3832+
if (HasDefaultedCopyConstructor(decl)) {
38523833
struct_attrs.emplace_back("Clone");
38533834
}
38543835

@@ -4040,7 +4021,8 @@ void Converter::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
40404021

40414022
StrCat(lhs_as_string, assign_operator, rhs_as_string);
40424023
if (!isVoid()) {
4043-
StrCat(token::kSemiColon, ConvertRValue(lhs));
4024+
StrCat(token::kSemiColon,
4025+
isAddrOf() ? ConvertRValue(lhs) : ConvertFreshRValue(lhs));
40444026
}
40454027
}
40464028

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,21 @@ bool IsUserDefinedCopyConstructor(const clang::CXXConstructorDecl *ctor) {
263263
IsUserDefinedDecl(ctor);
264264
}
265265

266+
bool IsUserDefinedMoveConstructor(const clang::CXXConstructorDecl *ctor) {
267+
return ctor->isMoveConstructor() && ctor->isUserProvided() &&
268+
IsUserDefinedDecl(ctor);
269+
}
270+
271+
bool IsUserDefinedCopyOrMoveConstructor(const clang::CXXConstructorDecl *ctor) {
272+
return IsUserDefinedCopyConstructor(ctor) ||
273+
IsUserDefinedMoveConstructor(ctor);
274+
}
275+
276+
bool IsDefaultedMoveConstructor(const clang::CXXConstructorDecl *ctor) {
277+
return ctor->isMoveConstructor() && !ctor->isUserProvided() &&
278+
IsUserDefinedDecl(ctor->getParent());
279+
}
280+
266281
clang::CXXConstructorDecl *
267282
GetUserDefinedCopyConstructor(const clang::RecordDecl *decl) {
268283
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
@@ -277,11 +292,20 @@ GetUserDefinedCopyConstructor(const clang::RecordDecl *decl) {
277292
return nullptr;
278293
}
279294

280-
bool HasUserDefinedCopyConstructor(const clang::RecordDecl *decl) {
281-
return GetUserDefinedCopyConstructor(decl) != nullptr;
295+
bool HasDefaultedCopyConstructor(const clang::RecordDecl *decl) {
296+
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
297+
if (!cxx) {
298+
return true;
299+
}
300+
for (const auto *ctor : cxx->ctors()) {
301+
if (ctor->isCopyConstructor()) {
302+
return !ctor->isUserProvided() && !ctor->isDeleted();
303+
}
304+
}
305+
return !cxx->defaultedCopyConstructorIsDeleted();
282306
}
283307

284-
bool IsCopyConstructible(const clang::RecordDecl *decl) {
308+
bool HasCallableCopyConstructor(const clang::RecordDecl *decl) {
285309
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
286310
if (!cxx) {
287311
return true;
@@ -299,12 +323,13 @@ bool IsCopyConstructible(const clang::RecordDecl *decl) {
299323
}
300324

301325
bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor) {
302-
return ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 &&
326+
return !ctor->isCopyOrMoveConstructor() &&
327+
ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 &&
303328
ctor->getParamDecl(0)->getType()->isRValueReferenceType();
304329
}
305330

306331
bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor) {
307-
return !IsUserDefinedCopyConstructor(ctor) &&
332+
return !IsUserDefinedCopyOrMoveConstructor(ctor) &&
308333
(ctor->isCopyOrMoveConstructor() ||
309334
IsRValueConvertingConstructor(ctor));
310335
}
@@ -313,9 +338,13 @@ bool IsConvertibleCXXRecordDecl(const clang::CXXRecordDecl *decl) {
313338
return decl->isThisDeclarationADefinition() &&
314339
std::all_of(
315340
decl->method_begin(), decl->method_end(), [](auto *method) {
341+
auto *ctor = clang::dyn_cast<clang::CXXConstructorDecl>(method);
316342
return method->getDefinition() || method->isPureVirtual() ||
317343
method->getTemplateInstantiationPattern() ||
318-
method->getDescribedFunctionTemplate();
344+
method->getDescribedFunctionTemplate() ||
345+
(ctor ? ctor->isCopyOrMoveConstructor()
346+
: method->isCopyAssignmentOperator() ||
347+
method->isMoveAssignmentOperator());
319348
});
320349
}
321350

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,18 @@ bool IsOverloadedMethod(const clang::CXXMethodDecl *decl);
6565

6666
bool IsUserDefinedCopyConstructor(const clang::CXXConstructorDecl *ctor);
6767

68+
bool IsUserDefinedMoveConstructor(const clang::CXXConstructorDecl *ctor);
69+
70+
bool IsUserDefinedCopyOrMoveConstructor(const clang::CXXConstructorDecl *ctor);
71+
72+
bool IsDefaultedMoveConstructor(const clang::CXXConstructorDecl *ctor);
73+
6874
clang::CXXConstructorDecl *
6975
GetUserDefinedCopyConstructor(const clang::RecordDecl *decl);
7076

71-
bool HasUserDefinedCopyConstructor(const clang::RecordDecl *decl);
77+
bool HasCallableCopyConstructor(const clang::RecordDecl *decl);
7278

73-
bool IsCopyConstructible(const clang::RecordDecl *decl);
79+
bool HasDefaultedCopyConstructor(const clang::RecordDecl *decl);
7480

7581
bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor);
7682

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <clang/AST/RecordLayout.h>
77
#include <clang/Basic/OperatorKinds.h>
8+
#include <llvm/Support/ErrorHandling.h>
89

910
#include <algorithm>
1011
#include <format>
@@ -491,7 +492,7 @@ void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) {
491492
return;
492493
}
493494

494-
if (!IsCopyConstructible(cxx)) {
495+
if (!HasCallableCopyConstructor(cxx)) {
495496
return;
496497
}
497498

@@ -1880,12 +1881,23 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
18801881
}
18811882

18821883
auto *ctor = expr->getConstructor();
1883-
if (ctor->isMoveConstructor() || IsRValueConvertingConstructor(ctor)) {
1884+
if (IsRValueConvertingConstructor(ctor) ||
1885+
(ctor->isMoveConstructor() && !IsUserDefinedDecl(ctor->getParent()))) {
18841886
StrCat(ConvertLValue(expr->getArg(0)));
18851887
return false;
18861888
}
18871889

1888-
if (ctor->isCopyConstructor() && !IsUserDefinedCopyConstructor(ctor)) {
1890+
// Default move is translated using a bitwise .clone() implementation.
1891+
// Bitwise clone is only satisfied by default copy constructor. If the copy
1892+
// constructor is user defined, then default move calls copy constructor,
1893+
// which is wrong.
1894+
if (IsDefaultedMoveConstructor(ctor) &&
1895+
!HasDefaultedCopyConstructor(ctor->getParent())) {
1896+
llvm::report_fatal_error("defaulted move constructor without a fieldwise "
1897+
"copy constructor is not supported");
1898+
}
1899+
if (ctor->isCopyOrMoveConstructor() &&
1900+
!IsUserDefinedCopyOrMoveConstructor(ctor)) {
18891901
StrCat(PushSuppressIteratorClone::take(*this)
18901902
? ConvertRValue(expr->getArg(0))
18911903
: ConvertFreshRValue(expr->getArg(0)));
@@ -2632,7 +2644,8 @@ void ConverterRefCount::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
26322644
}
26332645
return;
26342646
}
2635-
if (!base->isLValue() && base->getType()->isRecordType()) {
2647+
if (!base->isLValue() && base->getType()->isRecordType() &&
2648+
!IsReferenceType(base->IgnoreImplicit())) {
26362649
PushConversionKind push(*this, ConversionKind::FullRefCount);
26372650
ufcs_receiver_ =
26382651
token::kRef + BoxValue(ConvertRValue(base)) + ".as_pointer()";

‎tests/lit/lit/formats/Cpp2RustTest.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ def translate(self):
131131
)
132132
out, err, returncode = lit.util.executeCommand(cmd)
133133

134+
if returncode != 0 and exp.should_not_translate:
135+
return (lit.Test.XFAIL, "")
136+
134137
if not self.rs_file.exists():
135138
return (
136139
exp.fail_code,

‎tests/unit/copy_move_defaulted.cpp‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// translation-fail
2+
#include <cassert>
3+
#include <utility>
4+
#include <vector>
5+
6+
struct Inner {
7+
int x;
8+
};
9+
10+
struct Explicit {
11+
int v;
12+
Inner inner;
13+
int arr[2];
14+
Explicit(int v) : v(v), inner{v * 10}, arr{v, v + 1} {}
15+
Explicit(const Explicit &) = default;
16+
Explicit(Explicit &&) = default;
17+
Explicit &operator=(const Explicit &) = default;
18+
Explicit &operator=(Explicit &&) = default;
19+
~Explicit() {}
20+
};
21+
22+
struct Implicit {
23+
int v;
24+
Inner inner;
25+
int arr[2];
26+
};
27+
28+
struct DefaultCopyUserMove {
29+
int v;
30+
DefaultCopyUserMove(int v) : v(v) {}
31+
DefaultCopyUserMove(const DefaultCopyUserMove &) = default;
32+
DefaultCopyUserMove(DefaultCopyUserMove &&o) : v(o.v) { o.v = 0; }
33+
DefaultCopyUserMove &operator=(const DefaultCopyUserMove &) = default;
34+
DefaultCopyUserMove &operator=(DefaultCopyUserMove &&o) {
35+
v = o.v;
36+
o.v = 0;
37+
return *this;
38+
}
39+
};
40+
41+
struct UserCopyDefaultMove {
42+
int v;
43+
UserCopyDefaultMove(int v) : v(v) {}
44+
UserCopyDefaultMove(const UserCopyDefaultMove &o) : v(o.v + 100) {}
45+
UserCopyDefaultMove(UserCopyDefaultMove &&) = default;
46+
UserCopyDefaultMove &operator=(const UserCopyDefaultMove &o) {
47+
v = o.v + 100;
48+
return *this;
49+
}
50+
UserCopyDefaultMove &operator=(UserCopyDefaultMove &&) = default;
51+
};
52+
53+
static bool same(const Explicit &a, const Explicit &b) {
54+
return a.v == b.v && a.inner.x == b.inner.x && a.arr[0] == b.arr[0] &&
55+
a.arr[1] == b.arr[1];
56+
}
57+
58+
int main() {
59+
Explicit a(1);
60+
Explicit b = a;
61+
Explicit c(a);
62+
Explicit d(std::move(a));
63+
assert(same(b, a) && same(c, a) && same(d, a));
64+
65+
Explicit e(2), f(3);
66+
e = b;
67+
f = std::move(c);
68+
assert(same(e, b) && same(f, c));
69+
Explicit g(4);
70+
g = e = f;
71+
assert(same(g, f) && same(e, f));
72+
73+
Implicit i{5, {50}, {5, 6}};
74+
Implicit j = i;
75+
Implicit k = std::move(i);
76+
assert(j.v == 5 && j.inner.x == 50 && j.arr[1] == 6);
77+
assert(i.v == 5 && k.v == 5);
78+
Implicit l{0, {0}, {0, 0}};
79+
l = j;
80+
assert(l.v == 5 && l.inner.x == 50 && l.arr[0] == 5);
81+
82+
std::vector<Explicit> vec;
83+
vec.push_back(b);
84+
vec.push_back(Explicit(9));
85+
assert(vec[0].v == 1 && vec[1].v == 9);
86+
87+
DefaultCopyUserMove m(7);
88+
DefaultCopyUserMove m1 = m;
89+
DefaultCopyUserMove m2 = std::move(m);
90+
assert(m1.v == 7 && m2.v == 7 && m.v == 0);
91+
DefaultCopyUserMove m3(1), m4(1);
92+
m3 = m1;
93+
m4 = std::move(m1);
94+
assert(m3.v == 7 && m4.v == 7 && m1.v == 0);
95+
96+
UserCopyDefaultMove u(8);
97+
UserCopyDefaultMove u1 = u;
98+
UserCopyDefaultMove u2 = std::move(u);
99+
assert(u1.v == 108 && u2.v == 8 && u.v == 8);
100+
UserCopyDefaultMove u3(1), u4(1);
101+
u3 = u2;
102+
u4 = std::move(u2);
103+
assert(u3.v == 108 && u4.v == 8);
104+
return 0;
105+
}

0 commit comments

Comments
 (0)