Skip to content

Commit 24703cc

Browse files
authored
Translate copy constructor and copy assignment (#342)
Copy constructors are translated in the `impl Self` block, they take no receiver and build the `Self` object using the provided argument. If the copy constructor is not defaulted and not deleted, both unsafe and refcount generate it and Clone calls it. ```rs impl Counted { // Copy constructor pub unsafe fn Counted_pconstCounted(o: *const Counted) -> Self { let mut this = Self { v: (*o).v }; copies_0.prefix_inc(); this } } impl Clone for Counted { // Clone implementation calls the copy constructor fn clone(&self) -> Self { unsafe { Counted::Counted_pconstCounted(self as *const Counted) } } } ``` If the copy constructor is defaulted, unsafe continues to derive Clone and refcount continues to generate a deep copy: ```rs fn clone(&self) -> Self { Self { field: Rc::new(RefCell::new(field.clone())); ... } } ``` Copy assignment is translated using `operator_assign`: ```rs impl PartialImpl for Ptr<Partial> { fn operator_assign(&self, o: Ptr<Partial>) -> Ptr<Partial> { if ((*self) == (o)) { return (*self).clone(); } ... return (*self).clone(); } } ``` And users of copy assignment are translated as `PartialImpl::operator_assign(&a.as_pointer(), b.as_pointer())`
1 parent e0d9a9a commit 24703cc

12 files changed

Lines changed: 1161 additions & 42 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -624,9 +624,9 @@ void Converter::EmitScopedDestructor(const clang::VarDecl *decl) {
624624
kDestructorName));
625625
}
626626

627-
static bool hasUserDefinedNonDefaultCopyOrMoveCtor(clang::CXXRecordDecl *decl) {
627+
static bool hasUserDefinedNonDefaultMoveCtor(clang::CXXRecordDecl *decl) {
628628
for (const auto *ctor : decl->ctors()) {
629-
if (ctor->isCopyConstructor() || ctor->isMoveConstructor()) {
629+
if (ctor->isMoveConstructor()) {
630630
auto source = ctor->getDefinition() ? ctor->getDefinition() : ctor;
631631
if (source->isUserProvided() && !source->isDefaulted()) {
632632
return true;
@@ -635,8 +635,7 @@ static bool hasUserDefinedNonDefaultCopyOrMoveCtor(clang::CXXRecordDecl *decl) {
635635
}
636636

637637
for (const auto *method : decl->methods()) {
638-
if (method->isCopyAssignmentOperator() ||
639-
method->isMoveAssignmentOperator()) {
638+
if (method->isMoveAssignmentOperator()) {
640639
auto source = method->getDefinition() ? method->getDefinition() : method;
641640
if (source->isUserProvided() && !source->isDefaulted()) {
642641
return true;
@@ -946,8 +945,8 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
946945
return false;
947946
}
948947

949-
if (hasUserDefinedNonDefaultCopyOrMoveCtor(decl)) {
950-
assert(0 && "unsupported user-defined copy ctor, move ctor");
948+
if (hasUserDefinedNonDefaultMoveCtor(decl)) {
949+
assert(0 && "unsupported user-defined move ctor");
951950
}
952951

953952
sema_->ForceDeclarationOfImplicitMembers(decl);
@@ -1048,23 +1047,31 @@ std::string Converter::GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl) {
10481047
return decl->isConst() ? "&self" : "&mut self";
10491048
}
10501049

1050+
std::string Converter::GetCtorName(clang::CXXConstructorDecl *decl) {
1051+
if (decl->isCopyConstructor()) {
1052+
return GetOverloadedFunctionName(decl);
1053+
}
1054+
return GetRecordName(decl->getParent()) +
1055+
(GetNumberOfConvertingCtors(decl->getParent()) != 1
1056+
? std::to_string(GetCtorIndex(decl))
1057+
: "");
1058+
}
1059+
10511060
bool Converter::VisitCXXConstructorDecl(clang::CXXConstructorDecl *decl) {
10521061
if (decl->isOutOfLine() || decl->isImplicit()) {
10531062
return false;
10541063
}
10551064
PushCurrFunction push_fn(*this, decl);
10561065

1057-
if (decl->isCopyOrMoveConstructor()) {
1058-
// FIXME: improve error handling
1059-
assert(0 && "user-defined copy or move constructor are not supported");
1066+
if (decl->isMoveConstructor()) {
1067+
assert(0 && "user-defined move constructor are not supported");
1068+
}
1069+
if (decl->isCopyConstructor() && !decl->doesThisDeclarationHaveABody()) {
1070+
return false;
10601071
}
10611072

10621073
ConvertFunctionQualifiers(decl);
1063-
auto ctor_name = GetRecordName(decl->getParent()) +
1064-
(GetNumberOfConvertingCtors(decl->getParent()) != 1
1065-
? std::to_string(GetCtorIndex(decl))
1066-
: "");
1067-
StrCat(keyword_unsafe_, keyword::kFn, ctor_name);
1074+
StrCat(keyword_unsafe_, keyword::kFn, GetCtorName(decl));
10681075
{
10691076
PushParen paren(*this);
10701077
ConvertFunctionParameters(decl);
@@ -3316,11 +3323,8 @@ void Converter::ConvertArrayCXXConstructExpr(clang::CXXConstructExpr *expr) {
33163323

33173324
void Converter::ConvertCXXConstructExprArgs(clang::CXXConstructExpr *expr) {
33183325
auto ctor = expr->getConstructor();
3319-
auto ctor_name = GetRecordName(ctor->getParent());
3320-
StrCat(ctor_name, token::kDoubleColon,
3321-
ctor_name + (GetNumberOfConvertingCtors(ctor->getParent()) != 1
3322-
? std::to_string(GetCtorIndex(ctor))
3323-
: ""));
3326+
StrCat(GetRecordName(ctor->getParent()), token::kDoubleColon,
3327+
GetCtorName(ctor));
33243328
PushParen paren(*this);
33253329

33263330
unsigned arg_idx = 0;
@@ -3359,9 +3363,7 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
33593363
}
33603364

33613365
auto *ctor = expr->getConstructor();
3362-
if (ctor->isCopyOrMoveConstructor() ||
3363-
(ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 &&
3364-
ctor->getParamDecl(0)->getType()->isRValueReferenceType())) {
3366+
if (IsPassThroughConstructor(ctor)) {
33653367
// Take suppress before recursing into the child.
33663368
bool suppress = PushSuppressIteratorClone::take(*this);
33673369
Convert(expr->getArg(0));
@@ -3823,15 +3825,12 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) {
38233825

38243826
std::vector<const char *> struct_attrs;
38253827

3826-
if (RecordHasCopyableFields(decl)) {
3828+
bool derive_clone =
3829+
IsCopyConstructible(decl) && !HasUserDefinedCopyConstructor(decl);
3830+
if (derive_clone && RecordHasCopyableFields(decl)) {
38273831
struct_attrs.emplace_back("Copy");
38283832
}
3829-
3830-
if (auto cxx_decl = clang::dyn_cast<clang::CXXRecordDecl>(decl)) {
3831-
if (!cxx_decl->defaultedCopyConstructorIsDeleted()) {
3832-
struct_attrs.emplace_back("Clone");
3833-
}
3834-
} else /* RecordDecl */ {
3833+
if (derive_clone) {
38353834
struct_attrs.emplace_back("Clone");
38363835
}
38373836

@@ -4220,7 +4219,23 @@ void Converter::AddOrdTrait(const clang::CXXRecordDecl *decl) {
42204219
ConvertOrdAndPartialOrdTraits(decl, eq, lt, cmp);
42214220
}
42224221

4223-
void Converter::AddCloneTrait(const clang::RecordDecl *decl) {}
4222+
void Converter::AddCloneTrait(const clang::RecordDecl *decl) {
4223+
auto *ctor = GetUserDefinedCopyConstructor(decl);
4224+
if (!ctor) {
4225+
return;
4226+
}
4227+
auto record_name = GetRecordName(decl);
4228+
StrCat(keyword::kImpl, "Clone for", record_name);
4229+
PushBrace impl_brace(*this);
4230+
StrCat("fn clone(&self) -> Self");
4231+
PushBrace fn_brace(*this);
4232+
auto source = ctor->getParamDecl(0)->getType().getNonReferenceType();
4233+
StrCat(std::format("unsafe {{ {}::{}(self as *const {}{}) }}", record_name,
4234+
GetCtorName(ctor), record_name,
4235+
source.isConstQualified()
4236+
? ""
4237+
: std::format(" as *mut {}", record_name)));
4238+
}
42244239

42254240
void Converter::AddDefaultTraitForUnion(const clang::RecordDecl *decl) {
42264241
StrCat(std::format("impl Default for {}", GetRecordName(decl)));

‎cpp2rust/converter/converter.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
132132
bool ConvertCXXMethodDecl(clang::CXXMethodDecl *decl);
133133
std::string GetMethodName(const clang::CXXMethodDecl *decl);
134134
virtual std::string GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl);
135+
std::string GetCtorName(clang::CXXConstructorDecl *decl);
135136
virtual void ConvertCXXRecordMethods(clang::CXXRecordDecl *decl);
136137
virtual void ConvertLateInstantiatedMethods(clang::CXXRecordDecl *decl);
137138
virtual std::string DestroyMembers(const clang::CXXRecordDecl *decl);

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,57 @@ bool IsOverloadedMethod(const clang::CXXMethodDecl *decl) {
258258
}) > 1;
259259
}
260260

261+
bool IsUserDefinedCopyConstructor(const clang::CXXConstructorDecl *ctor) {
262+
return ctor->isCopyConstructor() && ctor->isUserProvided() &&
263+
IsUserDefinedDecl(ctor);
264+
}
265+
266+
clang::CXXConstructorDecl *
267+
GetUserDefinedCopyConstructor(const clang::RecordDecl *decl) {
268+
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
269+
if (!cxx) {
270+
return nullptr;
271+
}
272+
for (auto *ctor : cxx->ctors()) {
273+
if (IsUserDefinedCopyConstructor(ctor) && ctor->getDefinition()) {
274+
return ctor;
275+
}
276+
}
277+
return nullptr;
278+
}
279+
280+
bool HasUserDefinedCopyConstructor(const clang::RecordDecl *decl) {
281+
return GetUserDefinedCopyConstructor(decl) != nullptr;
282+
}
283+
284+
bool IsCopyConstructible(const clang::RecordDecl *decl) {
285+
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
286+
if (!cxx) {
287+
return true;
288+
}
289+
if (!cxx->hasUserDeclaredCopyConstructor()) {
290+
return !cxx->defaultedCopyConstructorIsDeleted();
291+
}
292+
for (const auto *ctor : cxx->ctors()) {
293+
if (ctor->isCopyConstructor() && !ctor->isDeleted() &&
294+
ctor->getDefinition()) {
295+
return true;
296+
}
297+
}
298+
return false;
299+
}
300+
301+
bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor) {
302+
return ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 &&
303+
ctor->getParamDecl(0)->getType()->isRValueReferenceType();
304+
}
305+
306+
bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor) {
307+
return !IsUserDefinedCopyConstructor(ctor) &&
308+
(ctor->isCopyOrMoveConstructor() ||
309+
IsRValueConvertingConstructor(ctor));
310+
}
311+
261312
bool IsConvertibleCXXRecordDecl(const clang::CXXRecordDecl *decl) {
262313
return decl->isThisDeclarationADefinition() &&
263314
std::all_of(
@@ -746,6 +797,9 @@ bool RecordNeedsDestruction(const clang::CXXRecordDecl *decl) {
746797
}
747798

748799
bool IsEmittableMethod(clang::CXXMethodDecl *method) {
800+
if (method->isDeleted()) {
801+
return false;
802+
}
749803
if (clang::isa<clang::CXXDestructorDecl>(method)) {
750804
return GetUserDefinedDestructor(method->getParent()) &&
751805
method->isThisDeclarationADefinition();
@@ -767,8 +821,8 @@ bool IsEmittableMethod(clang::CXXMethodDecl *method) {
767821
}
768822

769823
bool IsMethodOnPtr(const clang::CXXMethodDecl *method) {
770-
if (method->isImplicit() || method->isStatic() || method->isVirtual() ||
771-
clang::isa<clang::CXXConstructorDecl>(method)) {
824+
if (method->isImplicit() || method->isDeleted() || method->isStatic() ||
825+
method->isVirtual() || clang::isa<clang::CXXConstructorDecl>(method)) {
772826
return false;
773827
}
774828
if (!IsUserDefinedDecl(method->getParent()) ||

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ bool IsOverloadedFunction(const clang::FunctionDecl *decl);
6363

6464
bool IsOverloadedMethod(const clang::CXXMethodDecl *decl);
6565

66+
bool IsUserDefinedCopyConstructor(const clang::CXXConstructorDecl *ctor);
67+
68+
clang::CXXConstructorDecl *
69+
GetUserDefinedCopyConstructor(const clang::RecordDecl *decl);
70+
71+
bool HasUserDefinedCopyConstructor(const clang::RecordDecl *decl);
72+
73+
bool IsCopyConstructible(const clang::RecordDecl *decl);
74+
75+
bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor);
76+
77+
bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor);
78+
6679
bool IsConvertibleCXXRecordDecl(const clang::CXXRecordDecl *decl);
6780

6881
bool IsConvertibleCXXMethodDecl(const clang::CXXMethodDecl *decl);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,19 @@ std::string ConverterRefCount::GetComparisonCall(
449449
lhs_ptr, rhs_ptr);
450450
}
451451

452+
void ConverterRefCount::EmitShallowCopy(const clang::RecordDecl *decl) {
453+
StrCat("Rc::new");
454+
PushParen rc_paren(*this);
455+
StrCat("RefCell::new");
456+
PushParen cell_paren(*this);
457+
StrCat(GetRecordName(decl));
458+
PushBrace init_brace(*this);
459+
for (auto *field : decl->fields()) {
460+
auto name = GetNamedDeclAsString(field);
461+
StrCat(std::format("{0}: self.{0}.clone(),", name));
462+
}
463+
}
464+
452465
void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) {
453466
auto record_name = GetRecordName(decl);
454467

@@ -478,18 +491,26 @@ void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) {
478491
return;
479492
}
480493

481-
if (cxx->defaultedCopyConstructorIsDeleted()) {
494+
if (!IsCopyConstructible(cxx)) {
482495
return;
483496
}
484497

485498
StrCat(keyword::kImpl, "Clone for", record_name, '{');
486499
StrCat("fn clone(&self) -> Self {");
487500

488-
for (auto ctor : cxx->ctors()) {
489-
if (ctor->isCopyConstructor()) {
490-
PushConversionKind push(*this, ConversionKind::FullRefCount);
491-
ConvertCXXConstructorBody(ctor);
492-
break;
501+
if (auto *ctor = GetUserDefinedCopyConstructor(cxx)) {
502+
StrCat(std::format("let __src: Value<{}> =", record_name));
503+
EmitShallowCopy(decl);
504+
StrCat(token::kSemiColon);
505+
StrCat(std::format("{}::{}(__src.as_pointer())", record_name,
506+
GetCtorName(ctor)));
507+
} else {
508+
for (auto ctor : cxx->ctors()) {
509+
if (ctor->isCopyConstructor()) {
510+
PushConversionKind push(*this, ConversionKind::FullRefCount);
511+
ConvertCXXConstructorBody(ctor);
512+
break;
513+
}
493514
}
494515
}
495516

@@ -1845,14 +1866,12 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
18451866
}
18461867

18471868
auto *ctor = expr->getConstructor();
1848-
if (ctor->isMoveConstructor() ||
1849-
(ctor->isConvertingConstructor(false) && ctor->getNumParams() == 1 &&
1850-
ctor->getParamDecl(0)->getType()->isRValueReferenceType())) {
1869+
if (ctor->isMoveConstructor() || IsRValueConvertingConstructor(ctor)) {
18511870
StrCat(ConvertLValue(expr->getArg(0)));
18521871
return false;
18531872
}
18541873

1855-
if (ctor->isCopyConstructor()) {
1874+
if (ctor->isCopyConstructor() && !IsUserDefinedCopyConstructor(ctor)) {
18561875
StrCat(PushSuppressIteratorClone::take(*this)
18571876
? ConvertRValue(expr->getArg(0))
18581877
: ConvertFreshRValue(expr->getArg(0)));

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class ConverterRefCount final : public Converter {
4141
std::string_view lhs,
4242
std::string_view rhs) override;
4343

44+
void EmitShallowCopy(const clang::RecordDecl *decl);
4445
void AddCloneTrait(const clang::RecordDecl *decl) override;
4546

4647
void AddByteReprTrait(const clang::RecordDecl *decl) override;

0 commit comments

Comments
 (0)