Skip to content

Commit 349ab5a

Browse files
committed
Keep Box fields boxed
1 parent 3ed38b5 commit 349ab5a

50 files changed

Lines changed: 1355 additions & 805 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/converter_refcount.cpp‎

Lines changed: 62 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,24 @@ static bool IsTrivialValue(const clang::Expr *expr) {
8989
// Whether expr accesses a field of a struct that is stored in the struct.
9090
static bool IsFieldAccess(const clang::Expr *expr) {
9191
auto *member = clang::dyn_cast<clang::MemberExpr>(expr);
92-
auto *field =
93-
member ? clang::dyn_cast<clang::FieldDecl>(member->getMemberDecl())
94-
: nullptr;
92+
auto *field = member
93+
? clang::dyn_cast<clang::FieldDecl>(member->getMemberDecl())
94+
: nullptr;
9595
return field && IsTranslatedStruct(field->getParent()) &&
9696
!field->getType()->isReferenceType();
9797
}
9898

9999
// Whether the field holds a Value of its own, instead of being stored in the
100-
// struct. Vectors are, so that pointers to their elements, and to the fields
101-
// of those, point into the vector and not the struct.
102-
static bool IsValueField(clang::ASTContext &ctx,
103-
const clang::ValueDecl *decl) {
100+
// struct. Arrays and vectors are, so that pointers to their elements, and to
101+
// the fields of those, point into the array and not the struct.
102+
static bool IsValueField(clang::ASTContext &ctx, const clang::ValueDecl *decl) {
104103
auto *field = clang::dyn_cast<clang::FieldDecl>(decl);
105-
return field && IsTranslatedStruct(field->getParent()) &&
106-
!field->getType()->isReferenceType() &&
107-
Mapper::Map(ctx, field->getType().getUnqualifiedType())
108-
.starts_with("Vec<");
104+
if (!field || !IsTranslatedStruct(field->getParent())) {
105+
return false;
106+
}
107+
auto type = field->getType();
108+
return !type->isReferenceType() &&
109+
(type->isConstantArrayType() || IsBoxedType(ctx, type));
109110
}
110111

111112
// Whether evaluating expr reads a field of a struct, and hence borrows the
@@ -596,15 +597,9 @@ std::string ConverterRefCount::GetShallowCopy(const clang::RecordDecl *decl,
596597
auto name = GetNamedDeclAsString(field);
597598
auto type = field->getType();
598599
auto value = std::format("{}.{}", src, name);
600+
// Value fields are shared.
599601
if (auto *record = type->getAsRecordDecl(); IsTranslatedStruct(record)) {
600602
value = GetShallowCopy(record, value);
601-
} else if (auto *arr = ctx_.getAsConstantArrayType(type);
602-
arr && IsTranslatedStruct(
603-
arr->getElementType()->getAsRecordDecl())) {
604-
auto elem = std::format("__e{}", src.size());
605-
value = std::format(
606-
"{}.iter().map(|{}| {}).collect()", value, elem,
607-
GetShallowCopy(arr->getElementType()->getAsRecordDecl(), elem));
608603
} else {
609604
value += ".clone()";
610605
}
@@ -684,8 +679,14 @@ void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) {
684679
PushBrace init_brace(*this);
685680
for (auto *field : decl->fields()) {
686681
auto name = GetNamedDeclAsString(field);
687-
StrCat(std::format("{}: {},", name,
688-
GetDeepCopy(field->getType(), "self." + name)));
682+
StrCat(std::format(
683+
"{}: {},", name,
684+
IsValueField(ctx_, field)
685+
? std::format(
686+
"Rc::new(RefCell::new({}))",
687+
GetDeepCopy(field->getType(),
688+
std::format("(*self.{}.borrow())", name)))
689+
: GetDeepCopy(field->getType(), "self." + name)));
689690
}
690691
return;
691692
}
@@ -809,9 +810,12 @@ void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
809810
for (auto *field : decl->fields()) {
810811
auto byte_off = layout.getFieldOffset(idx) / 8;
811812
auto byte_size = ctx_.getTypeSize(field->getType()) / 8;
812-
StrCat(std::format("self.{}.to_bytes(&mut buf[{}..{}]);",
813-
GetNamedDeclAsString(field), byte_off,
814-
byte_off + byte_size));
813+
auto name = GetNamedDeclAsString(field);
814+
StrCat(std::format("{}.to_bytes(&mut buf[{}..{}]);",
815+
IsValueField(ctx_, field)
816+
? std::format("(*self.{}.borrow())", name)
817+
: "self." + name,
818+
byte_off, byte_off + byte_size));
815819
++idx;
816820
}
817821
}
@@ -825,13 +829,14 @@ void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
825829
for (auto *field : decl->fields()) {
826830
auto byte_off = layout.getFieldOffset(idx) / 8;
827831
auto byte_size = ctx_.getTypeSize(field->getType()) / 8;
828-
PushConversionKind push(*this, IsValueField(ctx_, field)
829-
? ConversionKind::FullRefCount
830-
: ConversionKind::Pointee);
831-
StrCat(std::format("{}: <{}>::from_bytes(&buf[{}..{}]),",
832-
GetNamedDeclAsString(field),
833-
ToString(field->getType()), byte_off,
834-
byte_off + byte_size));
832+
PushConversionKind push(*this, ConversionKind::Pointee);
833+
auto value = std::format("<{}>::from_bytes(&buf[{}..{}])",
834+
ToString(field->getType()), byte_off,
835+
byte_off + byte_size);
836+
if (IsValueField(ctx_, field)) {
837+
value = std::format("Rc::new(RefCell::new({}))", value);
838+
}
839+
StrCat(std::format("{}: {},", GetNamedDeclAsString(field), value));
835840
++idx;
836841
}
837842
}
@@ -2045,8 +2050,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
20452050
std::string ConverterRefCount::ConvertRecordPlace(clang::MemberExpr *expr,
20462051
bool mut) {
20472052
auto *base = expr->getBase();
2048-
if (clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) &&
2049-
!ThisIsRustPtr()) {
2053+
if (clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) && !ThisIsRustPtr()) {
20502054
return std::string(keyword::kSelfValue);
20512055
}
20522056
if (expr->isArrow()) {
@@ -2084,11 +2088,12 @@ static bool IsCloneSource(const clang::Expr *expr) {
20842088
}
20852089
}
20862090
auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
2087-
auto *param = ref ? clang::dyn_cast<clang::ParmVarDecl>(ref->getDecl())
2088-
: nullptr;
2089-
auto *ctor = param ? clang::dyn_cast<clang::CXXConstructorDecl>(
2090-
param->getDeclContext())
2091-
: nullptr;
2091+
auto *param =
2092+
ref ? clang::dyn_cast<clang::ParmVarDecl>(ref->getDecl()) : nullptr;
2093+
auto *ctor =
2094+
param
2095+
? clang::dyn_cast<clang::CXXConstructorDecl>(param->getDeclContext())
2096+
: nullptr;
20922097
return ctor && ctor->isCopyConstructor() && ctor->isDefaulted() &&
20932098
param->getName().empty();
20942099
}
@@ -2196,8 +2201,7 @@ ConverterRefCount::FindStructPtr(clang::Expr *place, bool mut) {
21962201
}
21972202
}
21982203

2199-
std::optional<std::string>
2200-
ConverterRefCount::ConvertInStructPtr(
2204+
std::optional<std::string> ConverterRefCount::ConvertInStructPtr(
22012205
clang::Expr *place, bool mut, llvm::function_ref<std::string()> convert) {
22022206
auto sp = FindStructPtr(place, mut);
22032207
if (!sp || sp->nested) {
@@ -2232,21 +2236,8 @@ std::string ConverterRefCount::ConvertFieldPointer(clang::MemberExpr *expr) {
22322236
} else {
22332237
base_ptr = ConvertPointer(base);
22342238
}
2235-
auto str = std::format("field_ptr!({}, {})", base_ptr,
2236-
GetNamedDeclAsString(expr->getMemberDecl()));
2237-
2238-
// Pointers to arrays and containers either point to the whole object or
2239-
// to its elements. The type is inferred from the context, like for
2240-
// `as_pointer()`, unless we know which one is wanted.
2241-
if (isObject()) {
2242-
auto field_type = expr->getMemberDecl()->getType();
2243-
if (WantsElementPtr()) {
2244-
return std::format("({} as {})", str, ConvertPtrType(field_type));
2245-
}
2246-
PushConversionKind push(*this, ConversionKind::Pointee);
2247-
return std::format("({} as Ptr<{}>)", str, ToString(field_type));
2248-
}
2249-
return str;
2239+
return std::format("field_ptr!({}, {})", base_ptr,
2240+
GetNamedDeclAsString(expr->getMemberDecl()));
22502241
}
22512242

22522243
void ConverterRefCount::ConvertFieldAccess(clang::MemberExpr *expr) {
@@ -2256,8 +2247,8 @@ void ConverterRefCount::ConvertFieldAccess(clang::MemberExpr *expr) {
22562247

22572248
if (field_type->isReferenceType()) {
22582249
// The field holds a pointer to the referenced object.
2259-
auto str = std::format("{}.{}", ConvertRecordPlace(expr, /*mut=*/false),
2260-
name);
2250+
auto str =
2251+
std::format("{}.{}", ConvertRecordPlace(expr, /*mut=*/false), name);
22612252
if (isAddrOf()) {
22622253
StrCat(str);
22632254
computed_expr_type_ = ComputedExprType::Pointer;
@@ -2274,9 +2265,9 @@ void ConverterRefCount::ConvertFieldAccess(clang::MemberExpr *expr) {
22742265
auto sp = FindStructPtr(expr);
22752266
if (!sp || sp->nested) {
22762267
auto *object = expr->getBase()->IgnoreParenImpCasts();
2277-
bool borrows = !field_places_.contains(expr) && !IsCloneSource(object) &&
2278-
!(clang::isa<clang::CXXThisExpr>(object) &&
2279-
!ThisIsRustPtr());
2268+
bool borrows =
2269+
!field_places_.contains(expr) && !IsCloneSource(object) &&
2270+
!(clang::isa<clang::CXXThisExpr>(object) && !ThisIsRustPtr());
22802271
PushBrace block(*this, borrows && isAddrOf());
22812272
ConvertFieldPlace(expr, ConvertValueField(expr));
22822273
return;
@@ -2344,8 +2335,8 @@ void ConverterRefCount::ConvertFieldPlace(clang::MemberExpr *expr,
23442335
computed_expr_type_ = ComputedExprType::FreshPointer;
23452336
return;
23462337
}
2347-
StrCat(std::format("(*{}.{}())", place,
2348-
isRValue() ? "borrow" : "borrow_mut"));
2338+
StrCat(
2339+
std::format("(*{}.{}())", place, isRValue() ? "borrow" : "borrow_mut"));
23492340
SetValueFreshness(expr->getType());
23502341
}
23512342

@@ -3038,9 +3029,8 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
30383029
break;
30393030

30403031
case clang::OverloadedOperatorKind::OO_Subscript: {
3041-
if (ConvertWithHoistedIndex(expr, expr->getArg(0), expr->getArg(1), [&] {
3042-
ConvertCXXOperatorCallExpr(expr);
3043-
})) {
3032+
if (ConvertWithHoistedIndex(expr, expr->getArg(0), expr->getArg(1),
3033+
[&] { ConvertCXXOperatorCallExpr(expr); })) {
30443034
break;
30453035
}
30463036

@@ -3423,8 +3413,7 @@ bool ConverterRefCount::IsReferenceType(const clang::Expr *expr) const {
34233413
}
34243414

34253415
// Collects the ways placeholder `n` is accessed in `fragments`.
3426-
static void
3427-
CollectPlaceholderAccesses(
3416+
static void CollectPlaceholderAccesses(
34283417
const std::vector<TranslationRule::BodyFragment> &fragments, unsigned n,
34293418
std::vector<TranslationRule::Access> &accesses) {
34303419
for (const auto &frag : fragments) {
@@ -3487,7 +3476,7 @@ std::optional<std::string> ConverterRefCount::ConvertOnFieldReceiver(
34873476
auto sp = receiver_ph.access != TranslationRule::Access::kMove &&
34883477
!receiver->getType()->isPointerType() &&
34893478
!RuleRegistry::ParamIsPointer(ctx_, GetCalleeOrExpr(expr),
3490-
receiver_ph.n)
3479+
receiver_ph.n)
34913480
? FindStructPtr(receiver, receiver_ph.access !=
34923481
TranslationRule::Access::kBorrow)
34933482
: std::nullopt;
@@ -3823,14 +3812,15 @@ ConverterRefCount::DestroyMembers(const clang::CXXRecordDecl *decl) {
38233812
auto *elem =
38243813
field->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
38253814
assert(elem);
3815+
// The array is a Value of its own.
38263816
out += std::format(
3827-
"{{ let __p: Ptr<{3}> = field_ptr!(self, {0}); for __i in "
3828-
"0..__p.len() {{ {2}::{1}(&__p.offset(__i as isize)); }} }}\n",
3817+
"{{ let __p: Ptr<{3}> = self.with(|__s| __s.{0}.as_pointer()); for "
3818+
"__i in 0..__p.len() {{ {2}::{1}(&__p.offset(__i as isize)); }} }}\n",
38293819
name, kDestructorName, TraitName(elem), GetRecordName(elem));
38303820
} else {
3831-
out += std::format(
3832-
"{2}::{1}(&field_ptr!(self, {0}));\n", name, kDestructorName,
3833-
TraitName(field->getType()->getAsCXXRecordDecl()));
3821+
out += std::format("{2}::{1}(&field_ptr!(self, {0}));\n", name,
3822+
kDestructorName,
3823+
TraitName(field->getType()->getAsCXXRecordDecl()));
38343824
}
38353825
}
38363826
return out;

‎docs/src/codegen/types.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct Item {
3838
#[offset(0)]
3939
pub id: i32,
4040
#[offset(4)]
41-
pub name: Box<[u8]>,
41+
pub name: Value<Box<[u8]>>,
4242
#[offset(16)]
4343
pub refs: Value<Vec<i32>>,
4444
}
@@ -49,8 +49,8 @@ pub fn count_0(item: Item) -> i32 {
4949
```
5050

5151
Fields are stored inline in their struct, so a whole struct lives in a single
52-
`Value`, like the elements of an array; only vectors are `Value`s of their own
53-
(see [Boxing](types/boxing.md)). A pointer to a field records the allocation of
54-
the struct plus the byte offset of the field in it, which the `#[offset(N)]`
55-
attributes give (see
52+
`Value`, like the elements of an array; only arrays and vectors are `Value`s of
53+
their own (see [Boxing](types/boxing.md)). A pointer to a field records the
54+
allocation of the struct plus the byte offset of the field in it, which the
55+
`#[offset(N)]` attributes give (see
5656
[Pointers to fields](../runtime/rc.md#pointers-to-fields)).

‎docs/src/codegen/types/boxing.md‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ that owns the type pushes one before printing it:
1212

1313
- `FullRefCount`: pushed by variable declarations; `Convert(QualType)` wraps the
1414
result in `Value<...>`.
15-
- `Pointee`: pushed by field declarations; the bare type is printed, but arrays
16-
are boxed slices, as in a `Value`. Fields whose type maps to a `Vec`
17-
(`std::vector`, `std::string`) push `FullRefCount` instead (see below).
15+
- `Pointee`: pushed by field declarations; the bare type is printed. Fields that
16+
are arrays, or whose type maps to a `Vec` or a `Box` (`std::vector`,
17+
`std::string`), push `FullRefCount` instead (see below).
1818
- `Unboxed`: pushed by parameter lists, return types, and record names; the bare
1919
type is printed.
2020
- `Ptr`: pushed by a pointer type for its pointee; also printed bare.
@@ -25,8 +25,7 @@ The result by position:
2525
| ------------------------------------------- | ------------ | ------------- | -------------------- |
2626
| local variable, global | `Value<i32>` | `Value<Item>` | `Value<Box<[i32]>>` |
2727
| function parameter, return type | `i32` | `Item` | decays to `Ptr<i32>` |
28-
| struct field | `i32` | `Item` | `Box<[i32]>` |
29-
| struct field of type `std::vector<int>` | | | `Value<Vec<i32>>` |
28+
| struct field | `i32` | `Item` | `Value<Box<[i32]>>` |
3029
| pointee of `Ptr<T>`, element of a container | `i32` | `Item` | `Box<[i32]>` |
3130

3231
Parameters arrive unboxed and are re-boxed by the function preamble; return
@@ -58,10 +57,11 @@ the outer `Value<...>` of the declaration is added.
5857

5958
Struct fields are stored inline, so that a whole struct is a single allocation,
6059
and a pointer to a field records the struct's allocation and the field's byte
61-
offset (see [Pointers](pointers.md)). Vectors are the exception: a vector field
62-
is a `Value<Vec<T>>` of its own. The elements of a vector live outside the
63-
struct, and their number changes, so a pointer to an element, or to a field of
64-
an element, has the vector as its allocation instead of the struct:
60+
offset (see [Pointers](pointers.md)). Arrays and vectors are the exception: an
61+
array field is a `Value<Box<[T]>>` of its own, and a vector field a
62+
`Value<Vec<T>>`. A pointer to an element, or to a field of an element, then has
63+
the array or the vector as its allocation instead of the struct, and pointer
64+
arithmetic moves between elements as for any other array:
6565

6666
```cpp
6767
struct Holder { std::vector<Point> points; int n; };
@@ -78,7 +78,7 @@ pub struct Holder {
7878
(*(*h.borrow()).points.borrow_mut())[(0_usize) as usize].y = 5;
7979
```
8080

81-
A vector field is accessed like a local vector, through its own `borrow()` or
82-
`borrow_mut()`, and the struct is only borrowed immutably to reach it. As
83-
`Value` is shared on `clone()`, structs with vector fields implement `Clone` by
84-
copying the vectors, instead of deriving it.
81+
An array or vector field is accessed like a local one, through its own
82+
`borrow()` or `borrow_mut()`, and the struct is only borrowed immutably to reach
83+
it. As `Value` is shared on `clone()`, structs with such fields implement
84+
`Clone` by copying the arrays and vectors, instead of deriving it.

‎docs/src/codegen/types/pointers.md‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ pointer type is to `const`. Globals use `&raw mut x` so no reference to the
4747
an element is `&mut arr[i] as *mut T`.
4848

4949
Refcount model: `&x` becomes `x.as_pointer()`, which produces a `Ptr` holding a
50-
weak reference to the variable's `Value`. `&s.field` is
51-
`field_ptr!(s, field)`, and `&p->field` is `field_ptr!(p, field)`: a pointer to
52-
the field of the struct, made from the `Value` or the `Ptr` of the struct (see
50+
weak reference to the variable's `Value`. `&s.field` is `field_ptr!(s, field)`,
51+
and `&p->field` is `field_ptr!(p, field)`: a pointer to the field of the struct,
52+
made from the `Value` or the `Ptr` of the struct (see
5353
[Pointers to fields](../../runtime/rc.md#pointers-to-fields)). An array decays
5454
with `arr.as_pointer() as Ptr<T>`, a `Ptr` to element 0 of the whole array, and
5555
`&arr[i]` is that pointer offset by `i`.
@@ -87,18 +87,18 @@ of the dereference:
8787
- An rvalue use copies the value out. A scalar or pointer pointee is `p.read()`,
8888
and so is a whole record, `*p`. A field of a record pointee is read in a
8989
closure that borrows the record for its duration: `p->x` is
90-
`p.with(|__s: &S| __s.x)`, and `p->a[i].y` is
91-
`p.with(|__s: &S| __s.a[i].y)`. A method called on a field runs in the
92-
closure too, together with the rest of its rule: `p->v.size()` is
93-
`p.with(|__s: &S| (*__s.v.borrow()).len())`.
90+
`p.with(|__s: &S| __s.x)`, and `p->a[i].y` is `p.with(|__s: &S| __s.a[i].y)`.
91+
A method called on a field runs in the closure too, together with the rest of
92+
its rule: `p->v.size()` is `p.with(|__s: &S| (*__s.v.borrow()).len())`.
9493
- A write to a field of a record pointee is also done in a closure, which
9594
borrows the record mutably: `p->x = v` is
9695
`p.with_mut(|__s: &mut S| __s.x = v)`. Operands that may access memory are
9796
evaluated before the closure (`let __rhs = ...;`), so that they don't find the
9897
record borrowed. Fields that are [`Value`s of their own](boxing.md) only need
99-
the record borrowed immutably, `p.with(|__s: &S| (*__s.v.borrow_mut()).push(1))`.
100-
The same closures are used for references to records, `r.x`, and for elements
101-
of arrays of records, `a[i].x`.
98+
the record borrowed immutably,
99+
`p.with(|__s: &S| (*__s.v.borrow_mut()).push(1))`. The same closures are used
100+
for references to records, `r.x`, and for elements of arrays of records,
101+
`a[i].x`.
102102
- An address-of use prints `p` itself.
103103
- An lvalue use prints nothing at once. The converter records the pointer
104104
expression as a [pending dereference](../expressions/pending-deref.md), and
@@ -110,9 +110,9 @@ of the dereference:
110110

111111
`with` and `with_mut` work on any pointer, including a
112112
[reinterpreted](../../runtime/reinterpret.md) one, whose pointee is decoded from
113-
the bytes of the original allocation and, for `with_mut`, encoded back into
114-
them before the closure returns. A write through a reinterpreted pointer is
115-
hence visible right away through every other pointer to the same bytes.
113+
the bytes of the original allocation and, for `with_mut`, encoded back into them
114+
before the closure returns. A write through a reinterpreted pointer is hence
115+
visible right away through every other pointer to the same bytes.
116116

117117
## Arithmetic and comparison
118118

0 commit comments

Comments
 (0)