Skip to content

Commit 02cdd79

Browse files
authored
Translate classes with mutable fields (#387)
Classes with mutable fields can modify the mutable fields even in const methods. For this reason, the receiver of const methods in classes that contain mutable fields should be `&mut` instead of `&`.
1 parent b6f88a3 commit 02cdd79

6 files changed

Lines changed: 165 additions & 3 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ bool Converter::ConvertCXXMethodDecl(clang::CXXMethodDecl *decl) {
11231123
}
11241124

11251125
std::string Converter::GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl) {
1126-
return decl->isConst() ? "&self" : "&mut self";
1126+
return MethodNeedsMutableReceiver(decl) ? "&mut self" : "&self";
11271127
}
11281128

11291129
std::string Converter::GetCtorName(clang::CXXConstructorDecl *decl) {
@@ -3210,12 +3210,22 @@ void Converter::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
32103210
}
32113211
Buffer buf(*this);
32123212
PushExprKind push(*this, ExprKind::LValue);
3213-
StrCat(method->isConst() ? "&" : "&mut");
3213+
auto object_type = is_arrow ? base->getType()->getPointeeType()
3214+
: base->getType().getNonReferenceType();
3215+
bool cast_mut =
3216+
MethodNeedsMutableReceiver(method) && object_type.isConstQualified();
3217+
StrCat(MethodNeedsMutableReceiver(method) ? "&mut" : "&");
3218+
if (cast_mut) {
3219+
StrCat("*(&raw const");
3220+
}
32143221
if (is_arrow) {
32153222
ConvertArrow(base);
32163223
} else {
32173224
Convert(base);
32183225
}
3226+
if (cast_mut) {
3227+
StrCat(").cast_mut()");
3228+
}
32193229
ufcs_receiver_ = std::move(buf).str();
32203230
}
32213231

@@ -4451,7 +4461,7 @@ std::string Converter::GetComparisonCall(const clang::FunctionDecl *op,
44514461
auto record = GetRecordName(decl);
44524462
auto arg = std::format("{} as *const {}", rhs, record);
44534463
if (const auto *method = clang::dyn_cast<clang::CXXMethodDecl>(op)) {
4454-
auto recv = method->isConst()
4464+
auto recv = !MethodNeedsMutableReceiver(method)
44554465
? std::string(lhs)
44564466
: std::format("&mut *(&raw const *{}).cast_mut()", lhs);
44574467
return std::format("{}::{}({}, {})", GetUFCSName(method),

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,15 @@ bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor) {
402402
ctor->getParamDecl(0)->getType()->isRValueReferenceType();
403403
}
404404

405+
bool MethodNeedsMutableReceiver(const clang::CXXMethodDecl *method) {
406+
if (!method->isConst()) {
407+
return true;
408+
}
409+
return std::any_of(method->getParent()->field_begin(),
410+
method->getParent()->field_end(),
411+
[](const clang::FieldDecl *f) { return f->isMutable(); });
412+
}
413+
405414
bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor) {
406415
return !IsConvertibleCopyOrMoveConstructor(ctor) &&
407416
(ctor->isCopyOrMoveConstructor() ||

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor);
9797

9898
bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor);
9999

100+
bool MethodNeedsMutableReceiver(const clang::CXXMethodDecl *method);
101+
100102
bool IsConvertibleCXXRecordDecl(const clang::CXXRecordDecl *decl);
101103

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

‎tests/unit/class.cpp‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ struct Route {
3636
}
3737
};
3838

39+
struct Counter {
40+
int v;
41+
mutable int calls;
42+
43+
int Get() const {
44+
++calls;
45+
return v;
46+
}
47+
48+
bool operator==(const Counter &o) const {
49+
++calls;
50+
return v == o.v;
51+
}
52+
};
53+
3954
int RandomRoute(Route &route) {
4055
if (route.path.first % 2) {
4156
return route.path.SetFirst(route.path.SetSecond(10));
@@ -49,5 +64,15 @@ int main() {
4964
Route route2 = {{1, 0}, 10};
5065
double old_cost = route1.SetCost(route2.SetCost(15));
5166
assert(RandomRoute(route1) + RandomRoute(route2) + old_cost == 9);
67+
Counter c1{3, 0};
68+
const Counter c2{3, 0};
69+
const Counter *pc = &c1;
70+
assert(c1.Get() == 3);
71+
assert(c2.Get() == 3);
72+
assert(pc->Get() == 3);
73+
assert(c1 == c2);
74+
assert(c2 == c1);
75+
assert(c1.calls == 3);
76+
assert(c2.calls == 2);
5277
return 0;
5378
}

‎tests/unit/out/refcount/class.rs‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,55 @@ impl ByteRepr for Route {
6666
}
6767
}
6868
}
69+
#[derive(Default)]
70+
pub struct Counter {
71+
pub v: Value<i32>,
72+
pub calls: Value<i32>,
73+
}
74+
impl std::cmp::PartialEq for Counter {
75+
fn eq(&self, other: &Self) -> bool {
76+
{
77+
CounterImpl::operator_eq(
78+
&Rc::new(RefCell::new(Counter {
79+
v: self.v.clone(),
80+
calls: self.calls.clone(),
81+
}))
82+
.as_pointer(),
83+
Rc::new(RefCell::new(Counter {
84+
v: other.v.clone(),
85+
calls: other.calls.clone(),
86+
}))
87+
.as_pointer(),
88+
)
89+
}
90+
}
91+
}
92+
impl std::cmp::Eq for Counter {}
93+
impl Clone for Counter {
94+
fn clone(&self) -> Self {
95+
let __this: Value<Counter> = Rc::new(RefCell::new(Self {
96+
v: Rc::new(RefCell::new((*self.v.borrow()))),
97+
calls: Rc::new(RefCell::new((*self.calls.borrow()))),
98+
}));
99+
let this: Ptr<Counter> = __this.as_pointer();
100+
Rc::try_unwrap(__this).ok().unwrap().into_inner()
101+
}
102+
}
103+
impl ByteRepr for Counter {
104+
fn byte_size() -> usize {
105+
8
106+
}
107+
fn to_bytes(&self, buf: &mut [u8]) {
108+
(*self.v.borrow()).to_bytes(&mut buf[0..4]);
109+
(*self.calls.borrow()).to_bytes(&mut buf[4..8]);
110+
}
111+
fn from_bytes(buf: &[u8]) -> Self {
112+
Self {
113+
v: Rc::new(RefCell::new(<i32>::from_bytes(&buf[0..4]))),
114+
calls: Rc::new(RefCell::new(<i32>::from_bytes(&buf[4..8]))),
115+
}
116+
}
117+
}
69118
pub fn RandomRoute_0(route: Ptr<Route>) -> i32 {
70119
if (((*(*(*route.upgrade().deref()).path.borrow()).first.borrow()) % 2) != 0) {
71120
return ({
@@ -115,8 +164,41 @@ fn main_0() -> i32 {
115164
+ (*old_cost.borrow()))
116165
== 9_f64)
117166
);
167+
let c1: Value<Counter> = Rc::new(RefCell::new(Counter {
168+
v: Rc::new(RefCell::new(3)),
169+
calls: Rc::new(RefCell::new(0)),
170+
}));
171+
let c2: Value<Counter> = Rc::new(RefCell::new(Counter {
172+
v: Rc::new(RefCell::new(3)),
173+
calls: Rc::new(RefCell::new(0)),
174+
}));
175+
let pc: Value<Ptr<Counter>> = Rc::new(RefCell::new((c1.as_pointer())));
176+
assert!((({ CounterImpl::Get(&c1.as_pointer(),) }) == 3));
177+
assert!((({ CounterImpl::Get(&c2.as_pointer(),) }) == 3));
178+
assert!((({ CounterImpl::Get(&(*pc.borrow()),) }) == 3));
179+
assert!(({ CounterImpl::operator_eq(&c1.as_pointer(), c2.as_pointer(),) }));
180+
assert!(({ CounterImpl::operator_eq(&c2.as_pointer(), c1.as_pointer(),) }));
181+
assert!(((*(*c1.borrow()).calls.borrow()) == 3));
182+
assert!(((*(*c2.borrow()).calls.borrow()) == 2));
118183
return 0;
119184
}
185+
pub trait CounterImpl {
186+
fn Get(&self) -> i32;
187+
fn operator_eq(&self, o: Ptr<Counter>) -> bool;
188+
}
189+
impl CounterImpl for Ptr<Counter> {
190+
fn Get(&self) -> i32 {
191+
(*(*(*self).upgrade().deref()).calls.borrow_mut()).prefix_inc();
192+
return (*(*(*self).upgrade().deref()).v.borrow());
193+
}
194+
fn operator_eq(&self, o: Ptr<Counter>) -> bool {
195+
(*(*(*self).upgrade().deref()).calls.borrow_mut()).prefix_inc();
196+
return {
197+
let _lhs = (*(*(*self).upgrade().deref()).v.borrow());
198+
_lhs == (*(*o.upgrade().deref()).v.borrow())
199+
};
200+
}
201+
}
120202
pub trait PairImpl {
121203
fn NOP(&self);
122204
fn GetFirst(&self) -> i32;

‎tests/unit/out/unsafe/class.rs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,30 @@ impl Route {
5454
return old_cost;
5555
}
5656
}
57+
#[repr(C)]
58+
#[derive(Copy, Clone, Default)]
59+
pub struct Counter {
60+
pub v: i32,
61+
pub calls: i32,
62+
}
63+
impl Counter {
64+
pub unsafe fn Get(&mut self) -> i32 {
65+
self.calls.prefix_inc();
66+
return self.v;
67+
}
68+
pub unsafe fn operator_eq(&mut self, o: *const Counter) -> bool {
69+
self.calls.prefix_inc();
70+
return ((self.v) == ((*o).v));
71+
}
72+
}
73+
impl std::cmp::PartialEq for Counter {
74+
fn eq(&self, other: &Self) -> bool {
75+
unsafe {
76+
Counter::operator_eq(&mut *(&raw const *self).cast_mut(), other as *const Counter)
77+
}
78+
}
79+
}
80+
impl std::cmp::Eq for Counter {}
5781
pub unsafe fn RandomRoute_0(route: *mut Route) -> i32 {
5882
if ((((*route).path.first) % (2)) != 0) {
5983
return (unsafe {
@@ -101,6 +125,16 @@ unsafe fn main_0() -> i32 {
101125
+ (old_cost))
102126
== (9_f64))
103127
);
128+
let mut c1: Counter = Counter { v: 3, calls: 0 };
129+
let c2: Counter = Counter { v: 3, calls: 0 };
130+
let mut pc: *const Counter = (&mut c1 as *mut Counter).cast_const();
131+
assert!(((unsafe { Counter::Get(&mut *(&raw const c1).cast_mut(),) }) == (3)));
132+
assert!(((unsafe { Counter::Get(&mut *(&raw const c2).cast_mut(),) }) == (3)));
133+
assert!(((unsafe { Counter::Get(&mut *(&raw const (*pc)).cast_mut(),) }) == (3)));
134+
assert!((unsafe { Counter::operator_eq(&mut *(&raw const c1).cast_mut(), &c2,) }));
135+
assert!((unsafe { Counter::operator_eq(&mut *(&raw const c2).cast_mut(), &c1,) }));
136+
assert!(((c1.calls) == (3)));
137+
assert!(((c2.calls) == (2)));
104138
return 0;
105139
}
106140
pub unsafe fn __cpp2rust_init_globals() {}

0 commit comments

Comments
 (0)