Skip to content

Commit b907253

Browse files
committed
Reduce the number of temporary variabkes created by using braces
Rust 2024 treates the last expr as temporary, allowing us to write *x.borrow_mut() = { *x.borrow() +1 } without causing borrowing errors We must careful only with side effects and evaluation orders
1 parent f974321 commit b907253

201 files changed

Lines changed: 970 additions & 1816 deletions

File tree

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: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2928,24 +2928,18 @@ bool Converter::VisitUnaryOperator(clang::UnaryOperator *expr) {
29282928
bool Converter::VisitStmtExpr(clang::StmtExpr *expr) {
29292929
auto *body = expr->getSubStmt();
29302930
PushBrace brace(*this);
2931-
auto stmts = body->body();
2932-
size_t n = static_cast<size_t>(stmts.end() - stmts.begin());
2933-
size_t i = 0;
2934-
for (auto *s : stmts) {
2935-
++i;
2936-
if (i == n) {
2937-
if (auto *tail = clang::dyn_cast<clang::Expr>(s)) {
2938-
EmitStmtExprTail(tail);
2939-
continue;
2940-
}
2931+
for (auto *s : body->body()) {
2932+
// The last expression is the value of the block, so it has no semicolon.
2933+
auto *tail = clang::dyn_cast<clang::Expr>(s);
2934+
if (tail && s == body->body_back()) {
2935+
Convert(tail);
2936+
} else {
2937+
Convert(s);
29412938
}
2942-
Convert(s);
29432939
}
29442940
return false;
29452941
}
29462942

2947-
void Converter::EmitStmtExprTail(clang::Expr *tail) { Convert(tail); }
2948-
29492943
bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
29502944
StrCat(keyword::kIf);
29512945
ConvertCondition(expr->getCond());
@@ -3896,9 +3890,7 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) {
38963890
if (needs_switch_macro) {
38973891
StrCat("match", ToString(stmt->getCond()));
38983892
} else {
3899-
StrCat(
3900-
std::format("let __match_cond = {};", ConvertRValue(stmt->getCond())));
3901-
StrCat("match __match_cond");
3893+
StrCat(std::format("match {{ {} }}", ConvertRValue(stmt->getCond())));
39023894
}
39033895

39043896
PushBrace match_brace(*this);

‎cpp2rust/converter/converter.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
402402

403403
virtual bool VisitStmtExpr(clang::StmtExpr *expr);
404404

405-
virtual void EmitStmtExprTail(clang::Expr *tail);
406-
407405
virtual bool VisitConditionalOperator(clang::ConditionalOperator *expr);
408406

409407
virtual bool VisitDeclRefExpr(clang::DeclRefExpr *expr);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,15 +1558,9 @@ bool ConverterRefCount::VisitUnaryExprOrTypeTraitExpr(
15581558

15591559
bool ConverterRefCount::VisitStmtExpr(clang::StmtExpr *expr) {
15601560
PushConversionKind push(*this, ConversionKind::FullRefCount);
1561-
return Converter::VisitStmtExpr(expr);
1562-
}
1563-
1564-
void ConverterRefCount::EmitStmtExprTail(clang::Expr *tail) {
1565-
StrCat("let __result = ");
1566-
Convert(tail);
1567-
StrCat(token::kSemiColon);
1568-
StrCat("__result");
1569-
SetFreshType(tail->getType());
1561+
Converter::VisitStmtExpr(expr);
1562+
SetFreshType(expr->getType());
1563+
return false;
15701564
}
15711565

15721566
void ConverterRefCount::ConvertBinaryOperator(clang::BinaryOperator *expr) {
@@ -1580,8 +1574,7 @@ void ConverterRefCount::ConvertBinaryOperator(clang::BinaryOperator *expr) {
15801574
assign && GetSafeTypeAsString(lhs_type) !=
15811575
GetSafeTypeAsString(assign->getComputationResultType())) {
15821576
auto computation_result_type = assign->getComputationResultType();
1583-
PushBrace brace(*this);
1584-
StrCat(keyword::kLet, "rhs_0", token::kAssign);
1577+
std::optional<Buffer> buf(std::in_place, *this);
15851578
if (IsUnsignedArithOp(assign)) {
15861579
PushParen outer(*this);
15871580
{
@@ -1607,15 +1600,16 @@ void ConverterRefCount::ConvertBinaryOperator(clang::BinaryOperator *expr) {
16071600
} else {
16081601
ConvertCast(lhs_type);
16091602
}
1610-
StrCat(token::kSemiColon);
1611-
EmitSetOrAssign(lhs, "rhs_0");
1603+
auto value = std::move(*buf).str();
1604+
buf.reset();
1605+
EmitCompoundSetOrAssign(lhs, rhs, value);
16121606
return;
16131607
}
16141608

16151609
if (IsUnsignedArithOp(expr)) {
1616-
PushBrace brace(*this, expr->isCompoundAssignmentOp());
1610+
std::optional<Buffer> buf;
16171611
if (expr->isCompoundAssignmentOp()) {
1618-
StrCat(keyword::kLet, "rhs_0", token::kAssign);
1612+
buf.emplace(*this);
16191613
}
16201614
{
16211615
PushParen paren(*this);
@@ -1627,8 +1621,9 @@ void ConverterRefCount::ConvertBinaryOperator(clang::BinaryOperator *expr) {
16271621
}
16281622
ConvertUnsignedArithBinaryOperator(expr, rhs);
16291623
if (expr->isCompoundAssignmentOp()) {
1630-
StrCat(token::kSemiColon);
1631-
EmitSetOrAssign(lhs, "rhs_0");
1624+
auto value = std::move(*buf).str();
1625+
buf.reset();
1626+
EmitCompoundSetOrAssign(lhs, rhs, value);
16321627
} else {
16331628
computed_expr_type_ = ComputedExprType::FreshValue;
16341629
}
@@ -2424,16 +2419,39 @@ void ConverterRefCount::EmitSetOrAssign(clang::Expr *lhs,
24242419
computed_expr_type_ = ComputedExprType::FreshValue;
24252420
}
24262421

2422+
bool ConverterRefCount::CanBraceAssignedValue(
2423+
clang::Expr *lhs, clang::Expr *rhs, std::string_view assign_operator) {
2424+
return (assign_operator == "=" || lhs->getType()->isArithmeticType()) &&
2425+
!lhs->HasSideEffects(ctx_) && !rhs->HasSideEffects(ctx_);
2426+
}
2427+
2428+
void ConverterRefCount::EmitCompoundSetOrAssign(clang::Expr *lhs,
2429+
clang::Expr *rhs,
2430+
std::string_view value) {
2431+
if (CanBraceAssignedValue(lhs, rhs, "=")) {
2432+
PushBrace brace(*this, isRValue());
2433+
EmitSetOrAssign(lhs, std::format("{{ {} }}", value));
2434+
return;
2435+
}
2436+
PushBrace brace(*this);
2437+
StrCat(keyword::kLet, "rhs_0", token::kAssign, value, token::kSemiColon);
2438+
EmitSetOrAssign(lhs, "rhs_0");
2439+
}
2440+
24272441
void ConverterRefCount::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
24282442
std::string_view assign_operator) {
24292443
auto rhs_as_string = ConvertFreshRValue(rhs, lhs->getType());
24302444

24312445
PushBrace brace(*this, isRValue());
24322446

24332447
if (MayCauseBorrowMutError(lhs, rhs)) {
2434-
StrCat(keyword::kLet, "__rhs", token::kAssign, rhs_as_string,
2435-
token::kSemiColon);
2436-
rhs_as_string = "__rhs";
2448+
if (CanBraceAssignedValue(lhs, rhs, assign_operator)) {
2449+
rhs_as_string = std::format("{{ {} }}", rhs_as_string);
2450+
} else {
2451+
StrCat(keyword::kLet, "__rhs", token::kAssign, rhs_as_string,
2452+
token::kSemiColon);
2453+
rhs_as_string = "__rhs";
2454+
}
24372455
}
24382456

24392457
if (assign_operator == "=") {
@@ -2492,7 +2510,7 @@ void ConverterRefCount::ConvertGenericBinaryOperator(
24922510

24932511
if (may_cause_borrow_mut_err) {
24942512
StrCat(std::format(
2495-
"{{ let _lhs = {}; _lhs {} {} }}",
2513+
"({{ {} }} {} {{ {} }})",
24962514
ConvertFreshRValue(
24972515
lhs, GetOperandImplicitConversionTarget(ctx_, expr, lhs, rhs)),
24982516
opcode,

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ class ConverterRefCount final : public Converter {
134134

135135
bool VisitStmtExpr(clang::StmtExpr *expr) override;
136136

137-
void EmitStmtExprTail(clang::Expr *tail) override;
138-
139137
bool VisitInitListExpr(clang::InitListExpr *expr) override;
140138

141139
bool VisitCXXStdInitializerListExpr(
@@ -335,6 +333,19 @@ class ConverterRefCount final : public Converter {
335333

336334
void EmitSetOrAssign(clang::Expr *lhs, std::string_view rhs);
337335

336+
// Whether an assigned value can be wrapped in braces to release its borrows
337+
// before lhs is borrowed, as Rust 2024 drops the temporaries of a block's
338+
// tail expression at the end of the block. This requires the value to be
339+
// evaluated before lhs (or the order to be unobservable); otherwise it must
340+
// be bound to a variable first.
341+
bool CanBraceAssignedValue(clang::Expr *lhs, clang::Expr *rhs,
342+
std::string_view assign_operator);
343+
344+
// Emits `lhs = value`, where value is the result of the compound assignment
345+
// `lhs op= rhs` and thus reads lhs.
346+
void EmitCompoundSetOrAssign(clang::Expr *lhs, clang::Expr *rhs,
347+
std::string_view value);
348+
338349
// If lhs is a direct reference to a global/static value (not a reference
339350
// type), emits `var.with(|rc| *rc.borrow_mut() <op> <rhs>)` and returns
340351
// true. This avoids cloning the Rc just to assign through it. Returns

‎tests/multi-file/defaulted_move_cross_tu/out/refcount/defaulted_move_cross_tu.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ impl Default for S {
5151
}
5252
}
5353
pub fn sum_0(s: Ptr<S>) -> i32 {
54-
return {
55-
let _lhs = {
56-
let _lhs = ((*s.with(|__s| __s.v.clone()).borrow()).len() as i32);
57-
_lhs + ((array_field_ptr!(s, n) as Ptr<i32>)
54+
return ({
55+
({ ((*s.with(|__s| __s.v.clone()).borrow()).len() as i32) } + {
56+
((array_field_ptr!(s, n) as Ptr<i32>)
5857
.offset((0) as isize)
5958
.read())
60-
};
61-
_lhs + ((array_field_ptr!(s, n) as Ptr<i32>)
59+
})
60+
} + {
61+
((array_field_ptr!(s, n) as Ptr<i32>)
6262
.offset((1) as isize)
6363
.read())
64-
};
64+
});
6565
}
6666
pub fn main() {
6767
__cpp2rust_init_globals();

‎tests/multi-file/friend_operator/out/refcount/friend_operator.rs‎

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,8 @@ fn main_0() -> i32 {
5858
return 0;
5959
}
6060
pub fn operator_eq_0(x: Ptr<S>, y: Ptr<S>) -> bool {
61-
return ({
62-
let _lhs = x.with(|__s| __s.a);
63-
_lhs == y.with(|__s| __s.a)
64-
}) && ({
65-
let _lhs = x.with(|__s| __s.b);
66-
_lhs == y.with(|__s| __s.b)
67-
});
61+
return ({ x.with(|__s| __s.a) } == { y.with(|__s| __s.a) })
62+
&& ({ x.with(|__s| __s.b) } == { y.with(|__s| __s.b) });
6863
}
6964
pub fn operator_ne_1(x: Ptr<S>, y: Ptr<S>) -> bool {
7065
return !({
@@ -74,16 +69,9 @@ pub fn operator_ne_1(x: Ptr<S>, y: Ptr<S>) -> bool {
7469
});
7570
}
7671
pub fn operator_lt_2(x: Ptr<S>, y: Ptr<S>) -> bool {
77-
return ({
78-
let _lhs = x.with(|__s| __s.a);
79-
_lhs < y.with(|__s| __s.a)
80-
}) || (({
81-
let _lhs = x.with(|__s| __s.a);
82-
_lhs == y.with(|__s| __s.a)
83-
}) && ({
84-
let _lhs = x.with(|__s| __s.b);
85-
_lhs < y.with(|__s| __s.b)
86-
}));
72+
return ({ x.with(|__s| __s.a) } < { y.with(|__s| __s.a) })
73+
|| (({ x.with(|__s| __s.a) } == { y.with(|__s| __s.a) })
74+
&& ({ x.with(|__s| __s.b) } < { y.with(|__s| __s.b) }));
8775
}
8876
pub fn compare_3(x: Ptr<S>, y: Ptr<S>) -> i32 {
8977
if ({

‎tests/ub/out/refcount/max.rs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ fn main_0() -> i32 {
2929
__tmp_1.as_pointer()
3030
}
3131
});
32-
return (({
33-
let _lhs = (a.read());
34-
_lhs == (b.read())
35-
}) as i32);
32+
return (({ (a.read()) } == { (b.read()) }) as i32);
3633
}
3734
pub fn __cpp2rust_init_globals() {}

‎tests/ub/out/refcount/ub4.rs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ use std::io::{Read, Seek, Write};
77
use std::os::fd::AsFd;
88
use std::rc::{Rc, Weak};
99
pub fn smaller_0(x1: Ptr<i32>, x2: Ptr<i32>) -> Ptr<i32> {
10-
return if ({
11-
let _lhs = (x1.read());
12-
_lhs < (x2.read())
13-
}) {
10+
return if ({ (x1.read()) } < { (x2.read()) }) {
1411
(x1).clone()
1512
} else {
1613
(x2).clone()

‎tests/ub/out/refcount/ub6.rs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ pub fn fill_1(arr: Ptr<Option<Value<Box<[Ptr<i32>]>>>>, n1: Ptr<i32>) {
3939
pub fn any_2(arr: Ptr<Option<Value<Box<[Ptr<i32>]>>>>, n1: Ptr<i32>) -> bool {
4040
let out: Value<bool> = Rc::new(RefCell::new(false));
4141
let i: Value<i32> = Rc::new(RefCell::new(0));
42-
'loop_: while {
43-
let _lhs = (*i.borrow());
44-
_lhs < (n1.read())
45-
} {
42+
'loop_: while ({ (*i.borrow()) } < { (n1.read()) }) {
4643
let __rhs = (*out.borrow())
4744
|| (((*arr.upgrade().deref()).as_ref().unwrap().borrow()
4845
[((*i.borrow()) as usize) as usize]

‎tests/unit/out/refcount/02_address_taken.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ fn main_0() -> i32 {
1616
(*b_ptr.borrow()).write(3);
1717
let b_ptr_ptr: Value<Ptr<Ptr<i32>>> = Rc::new(RefCell::new((b_ptr.as_pointer())));
1818
((*b_ptr_ptr.borrow()).read()).write(4);
19-
let __rhs = (((*b_ptr_ptr.borrow()).read()).read());
20-
(*b_ptr.borrow()).write(__rhs);
19+
(*b_ptr.borrow()).write({ (((*b_ptr_ptr.borrow()).read()).read()) });
2120
let offset: Value<usize> = Rc::new(RefCell::new(
2221
((((*b_ptr.borrow()).clone() - (*b_ptr.borrow()).clone()) as i64) as usize),
2322
));

0 commit comments

Comments
 (0)