Skip to content

Commit abd0dd3

Browse files
committed
Fix #549: std::swap and std::sort copy elements rather than moving them in place
Also, handle copy and move assignment operators
1 parent 5a43d06 commit abd0dd3

64 files changed

Lines changed: 3614 additions & 270 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎cpp2rust/converter/converter.cpp‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -793,8 +793,9 @@ bool Converter::RecordHasCopyableFields(const clang::RecordDecl *decl) {
793793
}
794794
}
795795

796-
// Look recursively into fields that are RecordDecl
797-
if (auto field_record = f->getType()->getAsRecordDecl()) {
796+
// Look recursively into fields that are RecordDecl, or arrays of them
797+
if (auto field_record =
798+
ctx_.getBaseElementType(f->getType())->getAsRecordDecl()) {
798799
if (!RecordDerivesCopy(field_record)) {
799800
return false;
800801
}
@@ -3411,7 +3412,8 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
34113412
if (is_reference_type || RuleRegistry::ReturnsPointer(ctx_, expr)) {
34123413
computed_expr_type_ = ComputedExprType::Pointer;
34133414
} else {
3414-
StrCat(token::kRef);
3415+
StrCat(token::kRef,
3416+
expr->getType().isConstQualified() ? "" : keyword_mut_);
34153417
computed_expr_type_ = ComputedExprType::FreshPointer;
34163418
}
34173419
StrCat(str);
@@ -4365,6 +4367,14 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) {
43654367
struct_attrs.emplace_back("MoveCtorUnsafe");
43664368
}
43674369

4370+
if (HasCopyAssignOperator(decl)) {
4371+
struct_attrs.emplace_back("CopyAssignUnsafe");
4372+
}
4373+
4374+
if (HasMoveAssignOperator(decl)) {
4375+
struct_attrs.emplace_back("MoveAssignUnsafe");
4376+
}
4377+
43684378
if (auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
43694379
cxx && RecordNeedsDestruction(cxx)) {
43704380
struct_attrs.emplace_back("DestructorUnsafe");

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -533,11 +533,61 @@ bool IsConvertibleMoveAssignment(const clang::CXXMethodDecl *method) {
533533
HasFieldWithMoveAssign(parent);
534534
}
535535

536+
static bool IsTranslatedCopyAssignment(const clang::CXXMethodDecl *method) {
537+
if (!method->isCopyAssignmentOperator() || method->isDeleted() ||
538+
method->isVirtual()) {
539+
return false;
540+
}
541+
return (method->isUserProvided() && IsUserDefinedDecl(method) &&
542+
method->hasBody()) ||
543+
IsConvertibleCopyAssignment(method);
544+
}
545+
546+
bool HasCopyAssignOperator(const clang::RecordDecl *decl) {
547+
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
548+
return cxx && llvm::any_of(cxx->methods(), [](auto *method) {
549+
return IsTranslatedCopyAssignment(method) &&
550+
CanUseCopyOrMoveName(method, "copy_assign");
551+
});
552+
}
553+
554+
bool HasMoveAssignOperator(const clang::RecordDecl *decl) {
555+
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
556+
return cxx && llvm::any_of(cxx->methods(), [](auto *method) {
557+
return IsConvertibleMoveAssignment(method) && !method->isVirtual() &&
558+
CanUseCopyOrMoveName(method, "move_assign");
559+
});
560+
}
561+
562+
// Whether a defaulted copy assignment of decl isn't an assignment of a clone
563+
// of the source, which runs the copy constructors of the record and of its
564+
// fields: when the record has a copy constructor, or a field (or element of
565+
// an array field) has a copy assignment operator.
566+
static bool NeedsDefaultedCopyAssignment(const clang::CXXRecordDecl *decl) {
567+
return GetUserDefinedCopyConstructor(decl) ||
568+
llvm::any_of(decl->fields(), [](const clang::FieldDecl *field) {
569+
auto *record =
570+
field->getType()->getBaseElementTypeUnsafe()->getAsRecordDecl();
571+
return record && HasCopyAssignOperator(record);
572+
});
573+
}
574+
575+
bool IsConvertibleCopyAssignment(const clang::CXXMethodDecl *method) {
576+
auto *parent = method->getParent();
577+
if (!method->isCopyAssignmentOperator() || method->isUserProvided() ||
578+
method->isDeleted() || !method->hasBody() ||
579+
!IsConvertibleImplicitMemberParent(parent)) {
580+
return false;
581+
}
582+
return NeedsDefaultedCopyAssignment(parent);
583+
}
584+
536585
bool IsConvertibleImplicitMember(const clang::CXXMethodDecl *method) {
537586
if (auto *ctor = clang::dyn_cast<clang::CXXConstructorDecl>(method)) {
538587
return IsConvertibleMoveConstructor(ctor);
539588
}
540-
return IsConvertibleMoveAssignment(method);
589+
return IsConvertibleMoveAssignment(method) ||
590+
IsConvertibleCopyAssignment(method);
541591
}
542592

543593
bool IsDefaultedMoveConstructor(const clang::CXXConstructorDecl *ctor) {
@@ -1263,7 +1313,7 @@ bool IsImplicitAssignmentCall(const clang::CallExpr *expr) {
12631313
if (method->isUserProvided() && IsUserDefinedDecl(method)) {
12641314
return false;
12651315
}
1266-
return !IsConvertibleMoveAssignment(method);
1316+
return !IsConvertibleImplicitMember(method);
12671317
}
12681318

12691319
const clang::CXXRecordDecl *AsLambdaClass(clang::QualType type) {
@@ -1421,7 +1471,7 @@ bool IsUserOperatorCall(const clang::CXXOperatorCallExpr *expr) {
14211471
return IsUserDefinedDecl(owner);
14221472
}
14231473
if (const auto *method = clang::dyn_cast<clang::CXXMethodDecl>(callee);
1424-
method && IsConvertibleMoveAssignment(method)) {
1474+
method && IsConvertibleImplicitMember(method)) {
14251475
return true;
14261476
}
14271477
if (!callee->isUserProvided() || !IsUserDefinedDecl(callee)) {
@@ -1554,6 +1604,11 @@ void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl) {
15541604
!HasDefaultedCopyAssignment(decl)) {
15551605
sema.DefineImplicitMoveAssignment(decl->getLocation(), method);
15561606
}
1607+
if (method->isCopyAssignmentOperator() && !method->isUserProvided() &&
1608+
!method->doesThisDeclarationHaveABody() && !method->isDeleted() &&
1609+
NeedsDefaultedCopyAssignment(decl)) {
1610+
sema.DefineImplicitCopyAssignment(decl->getLocation(), method);
1611+
}
15571612
}
15581613
auto define_defaulted_comparison = [&](clang::FunctionDecl *fn) {
15591614
if (!fn || !IsComparisonOperator(fn) || !fn->isDefaulted() ||

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ bool HasMoveFromConstructor(const clang::RecordDecl *decl);
100100

101101
bool IsConvertibleMoveAssignment(const clang::CXXMethodDecl *method);
102102

103+
bool IsConvertibleCopyAssignment(const clang::CXXMethodDecl *method);
104+
105+
// Whether decl has a copy assignment operator converted to a copy_assign
106+
// method.
107+
bool HasCopyAssignOperator(const clang::RecordDecl *decl);
108+
109+
// Whether decl has a move assignment operator converted to a move_assign
110+
// method.
111+
bool HasMoveAssignOperator(const clang::RecordDecl *decl);
112+
103113
bool IsConvertibleImplicitMember(const clang::CXXMethodDecl *method);
104114

105115
clang::CXXConstructorDecl *

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2520,6 +2520,14 @@ ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) {
25202520
attrs.emplace_back("MoveCtor");
25212521
}
25222522

2523+
if (HasCopyAssignOperator(decl)) {
2524+
attrs.emplace_back("CopyAssign");
2525+
}
2526+
2527+
if (HasMoveAssignOperator(decl)) {
2528+
attrs.emplace_back("MoveAssign");
2529+
}
2530+
25232531
if (auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
25242532
cxx && RecordNeedsDestruction(cxx)) {
25252533
attrs.emplace_back("Destructor");
@@ -2837,7 +2845,10 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
28372845
ConvertSubscriptIndex(expr->getArg(1))));
28382846
}
28392847

2840-
if (is_inner_boxed) {
2848+
// The pointer to the element is already a pointer to the whole inner
2849+
// container, which as_pointer can't tell from a pointer to its first
2850+
// element without a type.
2851+
if (is_inner_boxed && (!isObject() || WantsElementPtr())) {
28412852
StrCat(".as_pointer()");
28422853
if (!isObject()) {
28432854
StrCat(std::format("as Ptr<{}>)", ToString(expr->getType())));

‎libcc2rs-macros/src/assign.rs‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2022-present INESC-ID.
2+
// Distributed under the MIT license that can be found in the LICENSE file.
3+
4+
use proc_macro::TokenStream;
5+
use quote::{format_ident, quote};
6+
use syn::{DeriveInput, parse_macro_input};
7+
8+
// Implements libcc2rs::CopyAssign or MoveAssign (or their unsafe versions)
9+
// with the method of the same name, for `op` "copy" or "move".
10+
pub fn expand(input: TokenStream, op: &str, is_unsafe: bool) -> TokenStream {
11+
let input = parse_macro_input!(input as DeriveInput);
12+
let name = &input.ident;
13+
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
14+
let method = format_ident!("{}_assign", op);
15+
let is_move = op == "move";
16+
if is_unsafe {
17+
let tr = if is_move {
18+
quote!(::libcc2rs::MoveAssignUnsafe)
19+
} else {
20+
quote!(::libcc2rs::CopyAssignUnsafe)
21+
};
22+
let src_ty = if is_move {
23+
quote!(*mut Self)
24+
} else {
25+
quote!(*const Self)
26+
};
27+
quote! {
28+
impl #impl_generics #tr for #name #ty_generics #where_clause {
29+
unsafe fn #method(dst: *mut Self, src: #src_ty) {
30+
unsafe { (*dst).#method(src) };
31+
}
32+
}
33+
}
34+
} else {
35+
let tr = if is_move {
36+
quote!(::libcc2rs::MoveAssign)
37+
} else {
38+
quote!(::libcc2rs::CopyAssign)
39+
};
40+
quote! {
41+
impl #impl_generics #tr for #name #ty_generics #where_clause {
42+
fn #method(dst: &::libcc2rs::Ptr<Self>, src: ::libcc2rs::Ptr<Self>) {
43+
dst.#method(src);
44+
}
45+
}
46+
}
47+
}
48+
.into()
49+
}

‎libcc2rs-macros/src/lib.rs‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use proc_macro::TokenStream;
55

6+
mod assign;
67
mod byte_repr;
78
mod destructor;
89
mod fn_ptr_arg;
@@ -165,6 +166,38 @@ pub fn derive_move_ctor_unsafe(input: TokenStream) -> TokenStream {
165166
move_ctor::expand(input, true)
166167
}
167168

169+
// #[derive(CopyAssign, MoveAssign)]
170+
// pub struct S { ... }
171+
// impl SImpl for Ptr<S> {
172+
// fn copy_assign(&self, o: Ptr<S>) -> Ptr<S> { ... }
173+
// fn move_assign(&self, o: Ptr<S>) -> Ptr<S> { ... }
174+
// }
175+
//
176+
// Implements libcc2rs::CopyAssign and MoveAssign for S with its copy and move
177+
// assignment operators, such that std::swap assigns the values of type S
178+
// with them, as C++ does. CopyAssignUnsafe and MoveAssignUnsafe are the same
179+
// for the unsafe model, whose copy_assign and move_assign are methods of S.
180+
181+
#[proc_macro_derive(CopyAssign)]
182+
pub fn derive_copy_assign(input: TokenStream) -> TokenStream {
183+
assign::expand(input, "copy", false)
184+
}
185+
186+
#[proc_macro_derive(CopyAssignUnsafe)]
187+
pub fn derive_copy_assign_unsafe(input: TokenStream) -> TokenStream {
188+
assign::expand(input, "copy", true)
189+
}
190+
191+
#[proc_macro_derive(MoveAssign)]
192+
pub fn derive_move_assign(input: TokenStream) -> TokenStream {
193+
assign::expand(input, "move", false)
194+
}
195+
196+
#[proc_macro_derive(MoveAssignUnsafe)]
197+
pub fn derive_move_assign_unsafe(input: TokenStream) -> TokenStream {
198+
assign::expand(input, "move", true)
199+
}
200+
168201
// #[derive(Destructor)]
169202
// pub struct S { ... }
170203
// impl SImpl for Ptr<S> {

‎libcc2rs/src/capture.rs‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::rc::Rc;
2626
use crate::fn_ptr::{FnPtr, FnSig};
2727
use crate::rc::{AsPointer, Ptr, Value};
2828
use crate::reinterpret::ByteRepr;
29+
use crate::{Map, UnsafeMap};
2930

3031
// The C++ move constructor of a type, in the refcount model, for the types
3132
// whose move isn't a copy. Records implement it with #[derive(MoveCtor)].
@@ -41,6 +42,36 @@ pub trait MoveCtorUnsafe: Sized {
4142
unsafe fn move_ctor(src: *mut Self) -> Self;
4243
}
4344

45+
// The C++ copy assignment operator of a type, in the refcount model, for the
46+
// types whose copy assignment isn't an assignment of a clone. Records
47+
// implement it with #[derive(CopyAssign)].
48+
pub trait CopyAssign: Sized {
49+
fn copy_assign(dst: &Ptr<Self>, src: Ptr<Self>);
50+
}
51+
52+
// The C++ copy assignment operator of a type, in the unsafe model. Records
53+
// implement it with #[derive(CopyAssignUnsafe)].
54+
pub trait CopyAssignUnsafe: Sized {
55+
/// # Safety
56+
/// `dst` and `src` must point to live values.
57+
unsafe fn copy_assign(dst: *mut Self, src: *const Self);
58+
}
59+
60+
// The C++ move assignment operator of a type, in the refcount model, for the
61+
// types whose move assignment isn't an assignment of a moved value. Records
62+
// implement it with #[derive(MoveAssign)].
63+
pub trait MoveAssign: Sized {
64+
fn move_assign(dst: &Ptr<Self>, src: Ptr<Self>);
65+
}
66+
67+
// The C++ move assignment operator of a type, in the unsafe model. Records
68+
// implement it with #[derive(MoveAssignUnsafe)].
69+
pub trait MoveAssignUnsafe: Sized {
70+
/// # Safety
71+
/// `dst` and `src` must point to live values.
72+
unsafe fn move_assign(dst: *mut Self, src: *mut Self);
73+
}
74+
4475
impl<T: FnSig> MoveCtor for FnPtr<T> {
4576
fn move_ctor(src: Ptr<Self>) -> Self {
4677
src.with(FnPtr::move_from)
@@ -76,6 +107,8 @@ macro_rules! impl_move_ctor_by_take {
76107
impl_move_ctor_by_take!(
77108
<T> Vec<T>,
78109
<K, V> BTreeMap<K, V>,
110+
<K, T> Map<K, T>,
111+
<K, T> UnsafeMap<K, T>,
79112
);
80113

81114
// unique_ptr.

‎libcc2rs/src/lib.rs‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,17 @@ mod fn_ptr;
4040
pub use fn_ptr::{FnPtr, Generic};
4141

4242
mod capture;
43-
pub use capture::{Destructor, DestructorUnsafe, MoveCtor, MoveCtorUnsafe};
43+
pub use capture::{
44+
CopyAssign, CopyAssignUnsafe, Destructor, DestructorUnsafe, MoveAssign, MoveAssignUnsafe,
45+
MoveCtor, MoveCtorUnsafe,
46+
};
47+
48+
mod swap;
49+
// Used by cpp_swap! and cpp_swap_unsafe!.
50+
#[doc(hidden)]
51+
pub mod __swap {
52+
pub use crate::swap::*;
53+
}
4454
// Used by lambda! and lambda_unsafe!.
4555
#[doc(hidden)]
4656
pub mod __capture {
@@ -86,6 +96,7 @@ mod format;
8696
pub use format::*;
8797

8898
pub use libcc2rs_macros::{
89-
ByteRepr, Destructor, DestructorUnsafe, FnPtrArg, MoveCtor, MoveCtorUnsafe, Record, VaArg,
90-
goto, goto_block, lambda, lambda_unsafe, switch,
99+
ByteRepr, CopyAssign, CopyAssignUnsafe, Destructor, DestructorUnsafe, FnPtrArg, MoveAssign,
100+
MoveAssignUnsafe, MoveCtor, MoveCtorUnsafe, Record, VaArg, goto, goto_block, lambda,
101+
lambda_unsafe, switch,
91102
};

0 commit comments

Comments
 (0)