Skip to content

Commit 607543d

Browse files
committed
Unbox local vectors and issue straight [] accesses instead of going through Ptr
1 parent 1cc7173 commit 607543d

53 files changed

Lines changed: 870 additions & 979 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/models/boxed_vars.cpp‎

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,41 @@
55

66
#include <clang/AST/RecursiveASTVisitor.h>
77

8+
#include <algorithm>
89
#include <unordered_map>
910
#include <vector>
1011

1112
#include "converter/converter_lib.h"
1213
#include "converter/mapper.h"
14+
#include "converter/rules/registry.h"
1315

1416
namespace cpp2rust {
1517
namespace {
18+
// Whether type is translated to a Vec whose elements are not in Values:
19+
// std::vector and std::string of scalars or user-defined structs.
20+
bool IsFlatVec(clang::QualType type) {
21+
auto *record =
22+
clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
23+
type->getAsCXXRecordDecl());
24+
if (!record || !record->isInStdNamespace() ||
25+
(record->getName() != "vector" && record->getName() != "basic_string")) {
26+
return false;
27+
}
28+
auto element = record->getTemplateArgs()[0].getAsType();
29+
return element->isScalarType() ||
30+
(element->isStructureOrClassType() &&
31+
IsUserDefinedDecl(element->getAsRecordDecl()));
32+
}
33+
1634
// Whether decl may be stored without a Value, if its address is not taken:
17-
// scalars, arrays of non-arrays, user-defined structs and unique_ptrs, unless
18-
// they have a destructor, which is called with a pointer to them.
35+
// scalars, arrays of non-arrays, user-defined structs, unique_ptrs and
36+
// vectors, unless they have a destructor, which is called with a pointer to
37+
// them.
1938
bool CanUnbox(const clang::VarDecl *decl) {
2039
auto type = decl->getType();
21-
if (type->isConstantArrayType()) {
40+
if (IsFlatVec(type)) {
41+
// Vectors are flat Vecs.
42+
} else if (type->isConstantArrayType()) {
2243
if (type->getAsArrayTypeUnsafe()->getElementType()->isArrayType()) {
2344
return false;
2445
}
@@ -72,8 +93,9 @@ bool IsTrivialAssignment(clang::CXXOperatorCallExpr *expr) {
7293
class AddressTakenVisitor
7394
: public clang::RecursiveASTVisitor<AddressTakenVisitor> {
7495
public:
75-
explicit AddressTakenVisitor(std::unordered_set<const clang::VarDecl *> &vars)
76-
: address_taken_(vars) {}
96+
AddressTakenVisitor(clang::ASTContext &ctx,
97+
std::unordered_set<const clang::VarDecl *> &vars)
98+
: ctx_(ctx), address_taken_(vars) {}
7799

78100
bool shouldVisitTemplateInstantiations() const { return true; }
79101

@@ -110,12 +132,18 @@ class AddressTakenVisitor
110132
if (auto *source = GetTrivialCopySource(expr)) {
111133
AddValueUse(source);
112134
}
135+
AddRuleArgs(expr, expr->getArgs(), expr->getNumArgs());
136+
return true;
137+
}
138+
139+
bool VisitCallExpr(clang::CallExpr *expr) {
140+
AddRuleArgs(expr, expr->getArgs(), expr->getNumArgs());
113141
return true;
114142
}
115143

116144
bool VisitCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr) {
117145
if (IsTrivialAssignment(expr)) {
118-
AddValueUse(expr->getArg(0));
146+
AddValueUse(expr->getArg(0), /*write=*/true);
119147
AddValueUse(IgnoreNoOpCasts(expr->getArg(1)));
120148
}
121149
// Dereferencing a unique_ptr reads the pointer it holds; the object it
@@ -136,14 +164,14 @@ class AddressTakenVisitor
136164

137165
bool VisitBinaryOperator(clang::BinaryOperator *expr) {
138166
if (expr->isAssignmentOp()) {
139-
AddValueUse(expr->getLHS());
167+
AddValueUse(expr->getLHS(), /*write=*/true);
140168
}
141169
return true;
142170
}
143171

144172
bool VisitUnaryOperator(clang::UnaryOperator *expr) {
145173
if (expr->isIncrementDecrementOp()) {
146-
AddValueUse(expr->getSubExpr());
174+
AddValueUse(expr->getSubExpr(), /*write=*/true);
147175
}
148176
return true;
149177
}
@@ -165,21 +193,48 @@ class AddressTakenVisitor
165193
}
166194

167195
private:
168-
// An element of an array, or a field of a struct, is accessed without
169-
// making a pointer to it.
170-
void AddValueUse(clang::Expr *expr) {
196+
// The arguments of a call translated by a rule are passed by value or
197+
// borrowed, unless the rule takes a pointer to them.
198+
void AddRuleArgs(clang::Expr *expr, clang::Expr **args, unsigned num_args) {
199+
auto *rule = RuleRegistry::GetExprRule(ctx_, GetCalleeOrExpr(expr));
200+
if (!rule) {
201+
return;
202+
}
203+
// Variadic arguments are not considered.
204+
auto all_args = BuildUnifiedArgs(expr, args, num_args);
205+
for (unsigned i = 0; i < all_args.size() && i < rule->params.size(); ++i) {
206+
if (!rule->params[i].is_pointer()) {
207+
AddValueUse(IgnoreNoOpCasts(all_args[i]));
208+
}
209+
}
210+
}
211+
212+
// An element of an array or vector, or a field of a struct, is accessed
213+
// without making a pointer to it.
214+
void AddValueUse(clang::Expr *expr, bool write = false) {
171215
expr = expr->IgnoreParens();
172216
if (auto *member = clang::dyn_cast<clang::MemberExpr>(expr)) {
173217
if (!member->isArrow() &&
174218
clang::isa<clang::FieldDecl>(member->getMemberDecl())) {
175-
AddValueUse(member->getBase());
219+
AddValueUse(member->getBase(), write);
176220
}
177221
} else if (auto *subscript =
178222
clang::dyn_cast<clang::ArraySubscriptExpr>(expr)) {
179223
auto *cast =
180224
clang::dyn_cast<clang::ImplicitCastExpr>(subscript->getBase());
181225
if (cast && cast->getCastKind() == clang::CK_ArrayToPointerDecay) {
182-
AddValueUse(cast->getSubExpr());
226+
AddValueUse(cast->getSubExpr(), write);
227+
}
228+
} else if (auto *op = clang::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
229+
// Rust doesn't let the index of an element that is written to read the
230+
// vector, as the vector is borrowed mutably first.
231+
auto *vec = IgnoreNoOpCasts(op->getArg(0));
232+
if (op->getOperator() == clang::OO_Subscript &&
233+
IsFlatVec(vec->getType()) &&
234+
(!write || std::ranges::none_of(GetAllVars(vec), [&](auto *var) {
235+
return GetAllVars(op->getArg(1)).contains(var);
236+
}))) {
237+
AddValueUse(vec, write);
183238
}
184239
} else if (auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr)) {
185240
value_uses_.insert(ref);
@@ -193,14 +248,15 @@ class AddressTakenVisitor
193248
}
194249
}
195250

251+
clang::ASTContext &ctx_;
196252
std::unordered_set<const clang::VarDecl *> &address_taken_;
197253
// The references to variables that only access their value.
198254
std::unordered_set<const clang::DeclRefExpr *> value_uses_;
199255
};
200256
} // namespace
201257

202258
BoxedVars::BoxedVars(clang::ASTContext &ctx) {
203-
AddressTakenVisitor(address_taken_)
259+
AddressTakenVisitor(ctx, address_taken_)
204260
.TraverseDecl(ctx.getTranslationUnitDecl());
205261
}
206262

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ bool ConverterRefCount::IsUnboxedPlace(const clang::Expr *expr) const {
208208
return ref && IsUnboxedVar(ref->getDecl());
209209
}
210210

211+
bool ConverterRefCount::IsValueLocal(const clang::Expr *expr) const {
212+
auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr->IgnoreParenImpCasts());
213+
auto *var = ref ? clang::dyn_cast<clang::VarDecl>(ref->getDecl()) : nullptr;
214+
// Variables captured by reference are accessed through pointers.
215+
return var && !IsGlobalVar(var) && !IsUnboxedVar(var) &&
216+
!GetDeclRefType(curr_function_, ref, var)->isReferenceType();
217+
}
218+
211219
bool ConverterRefCount::IsFieldOfUnboxedPlace(
212220
const clang::MemberExpr *expr) const {
213221
return !expr->isArrow() && !expr->getBase()->getType()->isUnionType() &&
@@ -2766,6 +2774,37 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
27662774
IsBoxedType(ctx_, expr->getType().getNonReferenceType()) &&
27672775
IsBoxedType(ctx_, expr->getArg(0)->getType().getNonReferenceType());
27682776

2777+
// An element of an unboxed container is accessed directly, as is one of
2778+
// a container stored in a Value by borrowing the container, unless
2779+
// computing the index may access the container too, as it stays borrowed
2780+
// meanwhile.
2781+
auto *idx = expr->getArg(1);
2782+
bool is_unboxed =
2783+
IsUnboxedPlace(expr->getArg(0)) &&
2784+
clang::isa<clang::DeclRefExpr>(expr->getArg(0)->IgnoreParenImpCasts());
2785+
if ((isLValue() || isRValue()) && !is_inner_boxed &&
2786+
(is_unboxed ||
2787+
(IsValueLocal(expr->getArg(0)) && !idx->HasSideEffects(ctx_) &&
2788+
std::ranges::none_of(
2789+
GetAllVars(idx), [&](const clang::ValueDecl *var) {
2790+
return var->getType()->isPointerType() ||
2791+
var->getType()->isReferenceType() ||
2792+
GetAllVars(expr->getArg(0)).contains(var);
2793+
})))) {
2794+
// The index is a size_t, i.e., a usize.
2795+
auto str =
2796+
std::format("{}[{}]", ToString(expr->getArg(0)), ConvertRValue(idx));
2797+
// A copied element is read in a block, which ends the borrow.
2798+
if (!is_unboxed && isRValue() && TypeIsCopyable(expr->getType())) {
2799+
StrCat(std::format("{{ {} }}", str));
2800+
SetFreshType(expr->getType());
2801+
} else {
2802+
StrCat(str);
2803+
SetValueFreshness(expr->getType());
2804+
}
2805+
break;
2806+
}
2807+
27692808
if (isLValue()) {
27702809
PushConversionKind push_ck(*this, ConversionKind::Unboxed);
27712810
pending_deref_.set(

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ class ConverterRefCount final : public Converter {
425425
// Whether expr is an unboxed variable or a field of one, which is accessed
426426
// in place.
427427
bool IsUnboxedPlace(const clang::Expr *expr) const;
428+
// Whether expr is a local variable or parameter stored in a Value.
429+
bool IsValueLocal(const clang::Expr *expr) const;
428430
// Whether expr is a field of an unboxed variable, or of a field of one,
429431
// which is stored in place, even if it is a reference.
430432
bool IsFieldOfUnboxedPlace(const clang::MemberExpr *expr) const;

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@ pub struct S {
1717
pub n: Value<Box<[i32]>>,
1818
}
1919
impl S {
20-
pub fn new(x: i32) -> Self {
21-
let x: Value<i32> = Rc::new(RefCell::new(x));
20+
pub fn new(mut x: i32) -> Self {
2221
Self {
23-
v: Rc::new(RefCell::new(vec![
24-
(*x.borrow());
25-
((*x.borrow()) as usize) as usize
26-
])),
27-
n: Rc::new(RefCell::new(Box::new([(*x.borrow()), ((*x.borrow()) + 1)]))),
22+
v: Rc::new(RefCell::new(vec![x; (x as usize) as usize])),
23+
n: Rc::new(RefCell::new(Box::new([x, (x + 1)]))),
2824
}
2925
}
3026
pub fn move_from(_a0: Ptr<S>) -> Self {

‎tests/unit/out/refcount/07_unique.rs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ use std::io::prelude::*;
66
use std::io::{Read, Seek, Write};
77
use std::os::fd::AsFd;
88
use std::rc::{Rc, Weak};
9-
pub fn fn_0(u: Option<Value<i32>>) -> Option<Value<i32>> {
10-
let u: Value<Option<Value<i32>>> = Rc::new(RefCell::new(u));
11-
(*(*u.borrow()).as_ref().unwrap().borrow_mut()) = 10;
12-
return (*u.borrow_mut()).take();
9+
pub fn fn_0(mut u: Option<Value<i32>>) -> Option<Value<i32>> {
10+
(*u.as_ref().unwrap().borrow_mut()) = 10;
11+
return u.take();
1312
}
1413
pub fn main() {
1514
__cpp2rust_init_globals();

‎tests/unit/out/refcount/08_unique_array.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ pub fn main() {
1111
std::process::exit(main_0());
1212
}
1313
fn main_0() -> i32 {
14-
let g: Value<Option<Value<Box<[i32]>>>> = Rc::new(RefCell::new(Some(Rc::new(RefCell::new(
14+
let mut g: Option<Value<Box<[i32]>>> = Some(Rc::new(RefCell::new(
1515
(0..2_usize).map(|_| <i32>::default()).collect::<Box<[_]>>(),
16-
)))));
17-
(*g.borrow()).as_ref().unwrap().borrow_mut()[(0_usize) as usize] = 11;
18-
(*g.borrow()).as_ref().unwrap().borrow_mut()[(1_usize) as usize] = 12;
19-
let mut g_ptr: Ptr<i32> = (*g.borrow()).as_pointer();
16+
)));
17+
g.as_ref().unwrap().borrow_mut()[(0_usize) as usize] = 11;
18+
g.as_ref().unwrap().borrow_mut()[(1_usize) as usize] = 12;
19+
let mut g_ptr: Ptr<i32> = g.as_pointer();
2020
elem!(g_ptr, 0).write(13);
2121
elem!(g_ptr, 1).write(14);
2222
assert!(
23-
(((*g.borrow()).as_ref().unwrap().borrow()[(0_usize) as usize]
24-
+ (*g.borrow()).as_ref().unwrap().borrow()[(1_usize) as usize])
23+
((g.as_ref().unwrap().borrow()[(0_usize) as usize]
24+
+ g.as_ref().unwrap().borrow()[(1_usize) as usize])
2525
== 27)
2626
);
2727
return 0;

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,24 @@ pub fn main() {
2828
std::process::exit(main_0());
2929
}
3030
fn main_0() -> i32 {
31-
let arr: Value<Box<[NonCopy]>> = Rc::new(RefCell::new(
32-
(0..3)
33-
.map(|_| <NonCopy>::default())
34-
.collect::<Box<[NonCopy]>>(),
35-
));
36-
(*arr.borrow_mut())[(0) as usize].tag = 7;
31+
let mut arr: [NonCopy; 3] = std::array::from_fn::<_, 3, _>(|_| <NonCopy>::default());
32+
arr[(0) as usize].tag = 7;
3733
{
3834
let __a1 = 42;
39-
(*{ (*arr.borrow())[(1) as usize].data.clone() }.borrow_mut()).push(__a1)
35+
(*{ arr[(1) as usize].data.clone() }.borrow_mut()).push(__a1)
4036
};
41-
assert!(({ (*arr.borrow())[(0) as usize].tag } == 7));
42-
assert!(((*{ (*arr.borrow())[(1) as usize].data.clone() }.borrow()).len() == 1_usize));
37+
assert!(({ arr[(0) as usize].tag } == 7));
38+
assert!(((*{ arr[(1) as usize].data.clone() }.borrow()).len() == 1_usize));
4339
assert!(
4440
((elem!(
45-
({ (*arr.borrow())[(1) as usize].data.as_pointer() } as Ptr<i32>),
41+
({ arr[(1) as usize].data.as_pointer() } as Ptr<i32>),
4642
0_usize
4743
)
4844
.read())
4945
== 42)
5046
);
51-
assert!(({ (*arr.borrow())[(2) as usize].tag } == 0));
52-
assert!(((*{ (*arr.borrow())[(2) as usize].data.clone() }.borrow()).len() == 0_usize));
47+
assert!(({ arr[(2) as usize].tag } == 0));
48+
assert!(((*{ arr[(2) as usize].data.clone() }.borrow()).len() == 0_usize));
5349
return 0;
5450
}
5551
pub fn __cpp2rust_init_globals() {}

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,27 @@ pub fn main() {
1111
std::process::exit(main_0());
1212
}
1313
fn main_0() -> i32 {
14-
let b1: Value<u8> = Rc::new(RefCell::new(1_u8));
14+
let mut b1: u8 = 1_u8;
1515
let mut ushift1: u32 = 3_u32;
16-
let mut shl1: u8 = (*b1.borrow()) << ushift1;
16+
let mut shl1: u8 = b1 << ushift1;
1717
assert!((shl1 == ((8) as u8)));
1818
let mut ushift2: u32 = 2_u32;
1919
let mut shr1: u8 = shl1 >> ushift2;
2020
assert!((shr1 == ((2) as u8)));
2121
let mut ushift3: u32 = 5_u32;
2222
{
23-
let n_ = (*b1.borrow()) << ushift3;
24-
(*b1.borrow_mut()) = n_;
25-
(*b1.borrow())
23+
let n_ = b1 << ushift3;
24+
b1 = n_;
25+
b1
2626
};
27-
assert!(((*b1.borrow()) == ((32) as u8)));
27+
assert!((b1 == ((32) as u8)));
2828
let mut ushift4: u32 = 3_u32;
2929
{
30-
let n_ = (*b1.borrow()) >> ushift4;
31-
(*b1.borrow_mut()) = n_;
32-
(*b1.borrow())
30+
let n_ = b1 >> ushift4;
31+
b1 = n_;
32+
b1
3333
};
34-
assert!(((*b1.borrow()) == ((4) as u8)));
34+
assert!((b1 == ((4) as u8)));
3535
return 0;
3636
}
3737
pub fn __cpp2rust_init_globals() {}

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,32 +40,31 @@ fn main_0() -> i32 {
4040
false => -1,
4141
}
4242
};
43-
let s: Value<Vec<i8>> = Rc::new(RefCell::new({
43+
let mut s: Vec<i8> = {
4444
let mut __bytes = (buf.as_pointer() as Ptr<i8>).to_c_bytes();
4545
__bytes.push(0);
4646
__bytes
47-
}));
48-
println!("{}", ((*s.borrow()).len() - 1));
47+
};
48+
println!("{}", (s.len() - 1));
4949
println!(
5050
"{} {}",
5151
(buf.as_pointer() as Ptr::<i8>)
5252
.to_c_string_iterator()
5353
.count(),
5454
(buf.as_pointer() as Ptr::<i8>).with_c_str(|__lookup| {
55-
(*s.borrow())
56-
.iter()
57-
.take((*s.borrow()).len().saturating_sub(1))
55+
s.iter()
56+
.take(s.len().saturating_sub(1))
5857
.rposition(|&x| __lookup.contains(&x))
5958
.unwrap_or(usize::MAX)
6059
})
6160
);
6261
let mut lit: Ptr<i8> = Ptr::<i8>::from_string_literal(b"xy\0zw");
63-
let t: Value<Vec<i8>> = Rc::new(RefCell::new({
62+
let mut t: Vec<i8> = {
6463
let mut __bytes = lit.to_c_bytes();
6564
__bytes.push(0);
6665
__bytes
67-
}));
68-
println!("{}", ((*t.borrow()).len() - 1));
66+
};
67+
println!("{}", (t.len() - 1));
6968
return 0;
7069
}
7170
pub fn __cpp2rust_init_globals() {}

0 commit comments

Comments
 (0)