Skip to content

Commit 933bdee

Browse files
authored
Write mutable fields through const objects (#520)
1 parent a28e8bd commit 933bdee

8 files changed

Lines changed: 219 additions & 40 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3019,28 +3019,28 @@ bool Converter::ConvertIncAndDec(clang::UnaryOperator *expr) {
30193019
auto *sub_expr = expr->getSubExpr();
30203020
switch (opcode) {
30213021
case clang::UO_PostInc: {
3022-
PushExprKind push(*this, ExprKind::RValue);
3022+
PushExprKind push(*this, ExprKind::LValue);
30233023
Convert(sub_expr);
30243024
StrCat(".postfix_inc()");
30253025
SetFresh();
30263026
return true;
30273027
}
30283028
case clang::UO_PostDec: {
3029-
PushExprKind push(*this, ExprKind::RValue);
3029+
PushExprKind push(*this, ExprKind::LValue);
30303030
Convert(sub_expr);
30313031
StrCat(".postfix_dec()");
30323032
SetFresh();
30333033
return true;
30343034
}
30353035
case clang::UO_PreInc: {
3036-
PushExprKind push(*this, ExprKind::RValue);
3036+
PushExprKind push(*this, ExprKind::LValue);
30373037
Convert(sub_expr);
30383038
StrCat(".prefix_inc()");
30393039
SetFresh();
30403040
return true;
30413041
}
30423042
case clang::UO_PreDec: {
3043-
PushExprKind push(*this, ExprKind::RValue);
3043+
PushExprKind push(*this, ExprKind::LValue);
30443044
Convert(sub_expr);
30453045
StrCat(".prefix_dec()");
30463046
SetFresh();
@@ -3379,6 +3379,21 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
33793379
str = std::move(buf).str();
33803380
}
33813381

3382+
if (auto *field = clang::dyn_cast<clang::FieldDecl>(member);
3383+
field && field->isMutable()) {
3384+
auto *base = expr->getBase();
3385+
auto object_type = expr->isArrow() ? base->getType()->getPointeeType()
3386+
: base->getType().getNonReferenceType();
3387+
if (!isRValue() && object_type.isConstQualified()) {
3388+
if (isAddrOf()) {
3389+
StrCat(std::format("(&raw const {}).cast_mut()", str));
3390+
computed_expr_type_ = ComputedExprType::FreshPointer;
3391+
return false;
3392+
}
3393+
str = std::format("(*(&raw const {}).cast_mut())", str);
3394+
}
3395+
}
3396+
33823397
if (isAddrOf()) {
33833398
bool is_reference_type = member->getType()->isReferenceType();
33843399
if (auto *method = clang::dyn_cast<clang::CXXMethodDecl>(member)) {
@@ -3427,22 +3442,12 @@ void Converter::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
34273442
}
34283443
Buffer buf(*this);
34293444
PushExprKind push(*this, ExprKind::LValue);
3430-
auto object_type = is_arrow ? base->getType()->getPointeeType()
3431-
: base->getType().getNonReferenceType();
3432-
bool cast_mut =
3433-
MethodNeedsMutableReceiver(method) && object_type.isConstQualified();
34343445
StrCat(MethodNeedsMutableReceiver(method) ? "&mut" : "&");
3435-
if (cast_mut) {
3436-
StrCat("*(&raw const");
3437-
}
34383446
if (is_arrow) {
34393447
ConvertArrow(base);
34403448
} else {
34413449
Convert(base);
34423450
}
3443-
if (cast_mut) {
3444-
StrCat(").cast_mut()");
3445-
}
34463451
ufcs_receiver_ = std::move(buf).str();
34473452
}
34483453

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -609,12 +609,7 @@ bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor) {
609609
}
610610

611611
bool MethodNeedsMutableReceiver(const clang::CXXMethodDecl *method) {
612-
if (!method->isConst()) {
613-
return true;
614-
}
615-
return std::any_of(method->getParent()->field_begin(),
616-
method->getParent()->field_end(),
617-
[](const clang::FieldDecl *f) { return f->isMutable(); });
612+
return !method->isConst();
618613
}
619614

620615
bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor) {

‎tests/unit/class.cpp‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ struct Counter {
5151
}
5252
};
5353

54+
struct Tally {
55+
mutable int calls;
56+
57+
int SameNameBetweenConstAndNonConst() {
58+
calls += 1;
59+
return 1;
60+
}
61+
62+
int SameNameBetweenConstAndNonConst() const {
63+
calls += 10;
64+
return 2;
65+
}
66+
};
67+
5468
int RandomRoute(Route &route) {
5569
if (route.path.first % 2) {
5670
return route.path.SetFirst(route.path.SetSecond(10));
@@ -74,5 +88,13 @@ int main() {
7488
assert(c2 == c1);
7589
assert(c1.calls == 3);
7690
assert(c2.calls == 2);
91+
Tally t{0};
92+
const Tally ct{0};
93+
const Tally &rt = t;
94+
assert(t.SameNameBetweenConstAndNonConst() == 1);
95+
assert(ct.SameNameBetweenConstAndNonConst() == 2);
96+
assert(rt.SameNameBetweenConstAndNonConst() == 2);
97+
assert(t.calls == 11);
98+
assert(ct.calls == 10);
7799
return 0;
78100
}

‎tests/unit/copy_ctor.cpp‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ struct OutOfLine {
4747

4848
OutOfLine::OutOfLine(const OutOfLine &o) : v(o.v + 100) { ++copies; }
4949

50+
struct Tracked {
51+
mutable int copied_from = 0;
52+
Tracked() = default;
53+
Tracked(const Tracked &o) { ++o.copied_from; }
54+
Tracked &operator=(const Tracked &o) {
55+
++o.copied_from;
56+
return *this;
57+
}
58+
};
59+
60+
static void touch(const Tracked *t) { ++t->copied_from; }
61+
5062
static int by_value(Counted c) { return c.v; }
5163

5264
static Counted make(int v) {
@@ -108,5 +120,14 @@ int main() {
108120
NonConst n2(cn);
109121
assert(n1.mark == 1);
110122
assert(n2.mark == 10);
123+
124+
Tracked t1;
125+
Tracked t2(t1);
126+
t2 = t1;
127+
assert(t1.copied_from == 2);
128+
const Tracked ct;
129+
Tracked t3(ct);
130+
touch(&ct);
131+
assert(ct.copied_from == 2);
111132
return 0;
112133
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ impl std::cmp::PartialEq for Counter {
5050
}
5151
}
5252
impl std::cmp::Eq for Counter {}
53+
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
54+
#[byte_size(4)]
55+
pub struct Tally {
56+
#[offset(0)]
57+
pub calls: i32,
58+
}
5359
pub fn RandomRoute_0(route: Ptr<Route>) -> i32 {
5460
if ((route.with(|__s| __s.path.first) % 2) != 0) {
5561
return ({
@@ -105,6 +111,14 @@ fn main_0() -> i32 {
105111
assert!(({ CounterImpl::operator_eq(&c2.as_pointer(), c1.as_pointer(),) }));
106112
assert!(({ (*c1.borrow()).calls } == 3));
107113
assert!(({ (*c2.borrow()).calls } == 2));
114+
let t: Value<Tally> = Rc::new(RefCell::new(Tally { calls: 0 }));
115+
let ct: Value<Tally> = Rc::new(RefCell::new(Tally { calls: 0 }));
116+
let rt: Ptr<Tally> = t.as_pointer();
117+
assert!((({ TallyImpl::SameNameBetweenConstAndNonConst_1(&t.as_pointer(),) }) == 1));
118+
assert!((({ TallyImpl::SameNameBetweenConstAndNonConst_2(&ct.as_pointer(),) }) == 2));
119+
assert!((({ TallyImpl::SameNameBetweenConstAndNonConst_2(&rt,) }) == 2));
120+
assert!(({ (*t.borrow()).calls } == 11));
121+
assert!(({ (*ct.borrow()).calls } == 10));
108122
return 0;
109123
}
110124
pub trait CounterImpl {
@@ -168,4 +182,22 @@ impl RouteImpl for Ptr<Route> {
168182
return old_cost;
169183
}
170184
}
185+
pub trait TallyImpl {
186+
fn SameNameBetweenConstAndNonConst_1(&self) -> i32;
187+
fn SameNameBetweenConstAndNonConst_2(&self) -> i32;
188+
}
189+
impl TallyImpl for Ptr<Tally> {
190+
fn SameNameBetweenConstAndNonConst_1(&self) -> i32 {
191+
{
192+
field!((*self), calls).with_mut(|__v| *__v = *__v + 1)
193+
};
194+
return 1;
195+
}
196+
fn SameNameBetweenConstAndNonConst_2(&self) -> i32 {
197+
{
198+
field!((*self), calls).with_mut(|__v| *__v = *__v + 10)
199+
};
200+
return 2;
201+
}
202+
}
171203
pub fn __cpp2rust_init_globals() {}

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

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,39 @@ impl Clone for OutOfLine {
175175
}
176176
}
177177
impl OutOfLine {}
178-
pub fn by_value_1(mut c: Counted) -> i32 {
178+
#[derive(Record, ByteRepr, VaArg, FnPtrArg)]
179+
#[byte_size(4)]
180+
pub struct Tracked {
181+
#[offset(0)]
182+
pub copied_from: i32,
183+
}
184+
impl Tracked {
185+
pub fn copy_from(o: Ptr<Tracked>) -> Self {
186+
let __this: Tracked = Self { copied_from: 0 };
187+
field!(o, copied_from).with_mut(|__v| __v.prefix_inc());
188+
__this
189+
}
190+
}
191+
impl Clone for Tracked {
192+
fn clone(&self) -> Self {
193+
let __src: Value<Tracked> = Rc::new(RefCell::new(Tracked {
194+
copied_from: self.copied_from.clone(),
195+
}));
196+
Tracked::copy_from(__src.as_pointer())
197+
}
198+
}
199+
impl Default for Tracked {
200+
fn default() -> Self {
201+
{ Tracked { copied_from: 0 } }
202+
}
203+
}
204+
pub fn touch_1(mut t: Ptr<Tracked>) {
205+
field!(t, copied_from).with_mut(|__v| __v.prefix_inc());
206+
}
207+
pub fn by_value_2(mut c: Counted) -> i32 {
179208
return c.v;
180209
}
181-
pub fn make_2(mut v: i32) -> Counted {
210+
pub fn make_3(mut v: i32) -> Counted {
182211
let c: Value<Counted> = Rc::new(RefCell::new(Counted::new({ v })));
183212
return Counted::copy_from({ c.as_pointer() });
184213
}
@@ -193,9 +222,9 @@ fn main_0() -> i32 {
193222
let mut d: Counted = Counted::copy_from({ a.as_pointer() });
194223
assert!((copies_0.with(|rc| *rc.borrow()) == 3));
195224
assert!(((b.v == 1) && (c.v == 1)) && (d.v == 1));
196-
assert!((({ by_value_1(Counted::copy_from({ a.as_pointer() },),) }) == 1));
225+
assert!((({ by_value_2(Counted::copy_from({ a.as_pointer() },),) }) == 1));
197226
assert!((copies_0.with(|rc| *rc.borrow()) == 4));
198-
let mut e: Counted = ({ make_2(5) });
227+
let mut e: Counted = ({ make_3(5) });
199228
assert!((e.v == 5));
200229
assert!((copies_0.with(|rc| *rc.borrow()) == 5));
201230
let mut f: Counted = Counted::new({ 6 });
@@ -255,8 +284,25 @@ fn main_0() -> i32 {
255284
let mut n2: NonConst = NonConst::new_2({ cn.as_pointer() });
256285
assert!((n1.mark == 1));
257286
assert!((n2.mark == 10));
287+
let t1: Value<Tracked> = Rc::new(RefCell::new(<Tracked>::default()));
288+
let t2: Value<Tracked> = Rc::new(RefCell::new(Tracked::copy_from({ t1.as_pointer() })));
289+
({ TrackedImpl::copy_assign(&t2.as_pointer(), t1.as_pointer()) });
290+
assert!(({ (*t1.borrow()).copied_from } == 2));
291+
let ct: Value<Tracked> = Rc::new(RefCell::new(<Tracked>::default()));
292+
let mut t3: Tracked = Tracked::copy_from({ ct.as_pointer() });
293+
({ touch_1((ct.as_pointer())) });
294+
assert!(({ (*ct.borrow()).copied_from } == 2));
258295
return 0;
259296
}
297+
pub trait TrackedImpl {
298+
fn copy_assign(&self, o: Ptr<Tracked>) -> Ptr<Tracked>;
299+
}
300+
impl TrackedImpl for Ptr<Tracked> {
301+
fn copy_assign(&self, o: Ptr<Tracked>) -> Ptr<Tracked> {
302+
field!(o, copied_from).with_mut(|__v| __v.prefix_inc());
303+
return (*self).clone();
304+
}
305+
}
260306
pub fn __cpp2rust_init_globals() {
261307
let _ = copies_0.with(|_| ());
262308
}

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

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,36 @@ pub struct Counter {
6161
pub calls: i32,
6262
}
6363
impl Counter {
64-
pub unsafe fn Get(&mut self) -> i32 {
65-
self.calls.prefix_inc();
64+
pub unsafe fn Get(&self) -> i32 {
65+
(*(&raw const self.calls).cast_mut()).prefix_inc();
6666
return self.v;
6767
}
68-
pub unsafe fn operator_eq(&mut self, o: *const Counter) -> bool {
69-
self.calls.prefix_inc();
68+
pub unsafe fn operator_eq(&self, o: *const Counter) -> bool {
69+
(*(&raw const self.calls).cast_mut()).prefix_inc();
7070
return ((self.v) == ((*o).v));
7171
}
7272
}
7373
impl std::cmp::PartialEq for Counter {
7474
fn eq(&self, other: &Self) -> bool {
75-
unsafe {
76-
Counter::operator_eq(&mut *(&raw const *self).cast_mut(), other as *const Counter)
77-
}
75+
unsafe { Counter::operator_eq(self, other as *const Counter) }
7876
}
7977
}
8078
impl std::cmp::Eq for Counter {}
79+
#[repr(C)]
80+
#[derive(Copy, Clone, VaArg, FnPtrArg, Default)]
81+
pub struct Tally {
82+
pub calls: i32,
83+
}
84+
impl Tally {
85+
pub unsafe fn SameNameBetweenConstAndNonConst_1(&mut self) -> i32 {
86+
self.calls += 1;
87+
return 1;
88+
}
89+
pub unsafe fn SameNameBetweenConstAndNonConst_2(&self) -> i32 {
90+
(*(&raw const self.calls).cast_mut()) += 10;
91+
return 2;
92+
}
93+
}
8194
pub unsafe fn RandomRoute_0(route: *mut Route) -> i32 {
8295
if ((((*route).path.first) % (2)) != 0) {
8396
return (unsafe {
@@ -128,13 +141,21 @@ unsafe fn main_0() -> i32 {
128141
let mut c1: Counter = Counter { v: 3, calls: 0 };
129142
let c2: Counter = Counter { v: 3, calls: 0 };
130143
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,) }));
144+
assert!(((unsafe { Counter::Get(&c1,) }) == (3)));
145+
assert!(((unsafe { Counter::Get(&c2,) }) == (3)));
146+
assert!(((unsafe { Counter::Get(&(*pc),) }) == (3)));
147+
assert!((unsafe { Counter::operator_eq(&c1, &c2,) }));
148+
assert!((unsafe { Counter::operator_eq(&c2, &c1,) }));
136149
assert!(((c1.calls) == (3)));
137150
assert!(((c2.calls) == (2)));
151+
let mut t: Tally = Tally { calls: 0 };
152+
let ct: Tally = Tally { calls: 0 };
153+
let rt: *const Tally = &t;
154+
assert!(((unsafe { Tally::SameNameBetweenConstAndNonConst_1(&mut t,) }) == (1)));
155+
assert!(((unsafe { Tally::SameNameBetweenConstAndNonConst_2(&ct,) }) == (2)));
156+
assert!(((unsafe { Tally::SameNameBetweenConstAndNonConst_2(&(*rt),) }) == (2)));
157+
assert!(((t.calls) == (11)));
158+
assert!(((ct.calls) == (10)));
138159
return 0;
139160
}
140161
pub unsafe fn __cpp2rust_init_globals() {}

0 commit comments

Comments
 (0)