Skip to content

Commit acf54ed

Browse files
committed
Merge branch 'master' into rules-in-tu
2 parents 84c0099 + 607543d commit acf54ed

59 files changed

Lines changed: 1017 additions & 1151 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: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,46 @@
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, and user-defined structs, unless they have a
18-
// 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
}
2546
} else if (type->isStructureOrClassType()) {
26-
if (!IsUserDefinedDecl(type->getAsRecordDecl()) ||
47+
if ((!IsUserDefinedDecl(type->getAsRecordDecl()) && !IsUniquePtr(type)) ||
2748
TypeNeedsDestruction(type)) {
2849
return false;
2950
}
@@ -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,27 +132,46 @@ 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
}
149+
// Dereferencing a unique_ptr reads the pointer it holds; the object it
150+
// points to is not stored in the unique_ptr.
151+
switch (expr->getOperator()) {
152+
case clang::OO_Subscript:
153+
case clang::OO_Star:
154+
case clang::OO_Arrow:
155+
if (IsUniquePtr(expr->getArg(0)->getType())) {
156+
AddValueUse(IgnoreNoOpCasts(expr->getArg(0)));
157+
}
158+
break;
159+
default:
160+
break;
161+
}
121162
return true;
122163
}
123164

124165
bool VisitBinaryOperator(clang::BinaryOperator *expr) {
125166
if (expr->isAssignmentOp()) {
126-
AddValueUse(expr->getLHS());
167+
AddValueUse(expr->getLHS(), /*write=*/true);
127168
}
128169
return true;
129170
}
130171

131172
bool VisitUnaryOperator(clang::UnaryOperator *expr) {
132173
if (expr->isIncrementDecrementOp()) {
133-
AddValueUse(expr->getSubExpr());
174+
AddValueUse(expr->getSubExpr(), /*write=*/true);
134175
}
135176
return true;
136177
}
@@ -152,21 +193,48 @@ class AddressTakenVisitor
152193
}
153194

154195
private:
155-
// An element of an array, or a field of a struct, is accessed without
156-
// making a pointer to it.
157-
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) {
158215
expr = expr->IgnoreParens();
159216
if (auto *member = clang::dyn_cast<clang::MemberExpr>(expr)) {
160217
if (!member->isArrow() &&
161218
clang::isa<clang::FieldDecl>(member->getMemberDecl())) {
162-
AddValueUse(member->getBase());
219+
AddValueUse(member->getBase(), write);
163220
}
164221
} else if (auto *subscript =
165222
clang::dyn_cast<clang::ArraySubscriptExpr>(expr)) {
166223
auto *cast =
167224
clang::dyn_cast<clang::ImplicitCastExpr>(subscript->getBase());
168225
if (cast && cast->getCastKind() == clang::CK_ArrayToPointerDecay) {
169-
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);
170238
}
171239
} else if (auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr)) {
172240
value_uses_.insert(ref);
@@ -180,14 +248,15 @@ class AddressTakenVisitor
180248
}
181249
}
182250

251+
clang::ASTContext &ctx_;
183252
std::unordered_set<const clang::VarDecl *> &address_taken_;
184253
// The references to variables that only access their value.
185254
std::unordered_set<const clang::DeclRefExpr *> value_uses_;
186255
};
187256
} // namespace
188257

189258
BoxedVars::BoxedVars(clang::ASTContext &ctx) {
190-
AddressTakenVisitor(address_taken_)
259+
AddressTakenVisitor(ctx, address_taken_)
191260
.TraverseDecl(ctx.getTranslationUnitDecl());
192261
}
193262

‎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/alloc_array.rs‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ pub fn All_0(arr: Ptr<Option<Value<Box<[i32]>>>>, mut N: i32, mut element: i32)
1919
}
2020
((arr).clone() as Ptr<Option<Value<Box<[i32]>>>>).write((*all.borrow_mut()).take());
2121
}
22-
pub fn Consume_1(arr: Option<Value<Box<[i32]>>>, mut N: i32) -> i32 {
23-
let arr: Value<Option<Value<Box<[i32]>>>> = Rc::new(RefCell::new(arr));
22+
pub fn Consume_1(mut arr: Option<Value<Box<[i32]>>>, mut N: i32) -> i32 {
2423
let mut sum: i32 = 0;
2524
let mut i: i32 = -1_i32;
2625
'loop_: while (i.prefix_inc() < N) {
27-
sum += (*arr.borrow()).as_ref().unwrap().borrow()[(i as usize) as usize];
26+
sum += arr.as_ref().unwrap().borrow()[(i as usize) as usize];
2827
}
2928
return sum;
3029
}

‎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() {}

0 commit comments

Comments
 (0)