Skip to content

Commit 44a9617

Browse files
committed
fix
1 parent 38fcdc2 commit 44a9617

36 files changed

Lines changed: 775 additions & 300 deletions

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ static bool IsValueField(clang::ASTContext &ctx,
7373
return type->isConstantArrayType() || IsBoxedType(ctx, type);
7474
}
7575

76+
// Whether `expr` is an array field whose elements are accessed through
77+
// array_field_ptr!, such that they are found in the memory of the struct when
78+
// the struct is reinterpreted from other memory.
79+
static bool IsArrayFieldPtr(clang::ASTContext &ctx, const clang::Expr *expr) {
80+
auto *member =
81+
clang::dyn_cast<clang::MemberExpr>(expr->IgnoreParenImpCasts());
82+
auto *field =
83+
member ? clang::dyn_cast<clang::FieldDecl>(member->getMemberDecl())
84+
: nullptr;
85+
if (!field || field->getParent()->isUnion() ||
86+
Mapper::Contains(ctx, member)) {
87+
return false;
88+
}
89+
auto *type = field->getType()->getAsArrayTypeUnsafe();
90+
return type && !type->getElementType()->isArrayType();
91+
}
92+
7693
static bool IsPointerType(clang::ASTContext &ctx, clang::QualType type) {
7794
return type->isPointerType() || GetStrongestIteratorCategory(ctx, type) ==
7895
IteratorCategory::Contiguous;
@@ -421,7 +438,7 @@ bool ConverterRefCount::VisitArraySubscriptExpr(
421438
clang::ArraySubscriptExpr *expr) {
422439
auto *base = expr->getBase();
423440
if (base->IgnoreCasts()->getType()->isPointerType() ||
424-
IsUnionArrayMember(base) ||
441+
IsUnionArrayMember(base) || IsArrayFieldPtr(ctx_, base) ||
425442
(IsReferenceType(base) &&
426443
base->IgnoreCasts()->getType()->isArrayType())) {
427444
ConvertPointerSubscript(expr);
@@ -1542,8 +1559,23 @@ bool ConverterRefCount::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) {
15421559
} else if (expr->getSubExpr()->getType()->isPointerType() &&
15431560
!expr->getSubExpr()->isNullPointerConstant(
15441561
ctx_, clang::Expr::NPC_ValueDependentIsNull)) {
1545-
StrCat(std::format("{}.reinterpret_cast::<{}>()",
1546-
ToString(expr->getSubExpr()),
1562+
auto sub_expr = expr->getSubExpr();
1563+
if (auto *array_type =
1564+
ctx_.getAsArrayType(sub_expr->getType()->getPointeeType())) {
1565+
// A pointer to an array is a pointer to its first element.
1566+
auto element_type = array_type->getElementType();
1567+
PushConversionKind push(*this, ConversionKind::Unboxed);
1568+
StrCat(std::format("({} as Ptr<{}>)", ToString(sub_expr),
1569+
ToString(element_type)));
1570+
if (!ctx_.hasSameUnqualifiedType(
1571+
element_type, expr->getType()->getPointeeType())) {
1572+
StrCat(std::format(".reinterpret_cast::<{}>()",
1573+
ConvertPointeeType(expr->getType())));
1574+
}
1575+
computed_expr_type_ = ComputedExprType::FreshPointer;
1576+
return false;
1577+
}
1578+
StrCat(std::format("{}.reinterpret_cast::<{}>()", ToString(sub_expr),
15471579
ConvertPointeeType(expr->getType())));
15481580
computed_expr_type_ = ComputedExprType::FreshPointer;
15491581
return false;
@@ -1848,6 +1880,13 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
18481880
return false;
18491881
}
18501882

1883+
if (isAddrOf() && IsArrayFieldPtr(ctx_, expr)) {
1884+
StrCat(std::format("array_field_ptr!({}, {})", ConvertRecordPtr(expr),
1885+
GetNamedDeclAsString(member)));
1886+
computed_expr_type_ = ComputedExprType::FreshPointer;
1887+
return false;
1888+
}
1889+
18511890
std::string str;
18521891
if (known) {
18531892
str = GetMappedAsString(expr);
@@ -1893,18 +1932,7 @@ void ConverterRefCount::ConvertInlineField(clang::MemberExpr *expr) {
18931932
auto *base = expr->getBase();
18941933
if (isAddrOf()) {
18951934
// A pointer to the struct, offset to the field.
1896-
std::string ptr;
1897-
{
1898-
Buffer buf(*this);
1899-
PushExprKind push(*this, ExprKind::AddrOf);
1900-
if (expr->isArrow()) {
1901-
ConvertArrow(base);
1902-
} else {
1903-
Convert(base);
1904-
}
1905-
ptr = std::move(buf).str();
1906-
}
1907-
StrCat(std::format("field_ptr!({}, {})", ptr, name));
1935+
StrCat(std::format("field_ptr!({}, {})", ConvertRecordPtr(expr), name));
19081936
computed_expr_type_ = ComputedExprType::FreshPointer;
19091937
return;
19101938
}
@@ -1939,6 +1967,17 @@ void ConverterRefCount::ConvertInlineField(clang::MemberExpr *expr) {
19391967
SetValueFreshness(type);
19401968
}
19411969

1970+
std::string ConverterRefCount::ConvertRecordPtr(clang::MemberExpr *expr) {
1971+
Buffer buf(*this);
1972+
PushExprKind push(*this, ExprKind::AddrOf);
1973+
if (expr->isArrow()) {
1974+
ConvertArrow(expr->getBase());
1975+
} else {
1976+
Convert(expr->getBase());
1977+
}
1978+
return std::move(buf).str();
1979+
}
1980+
19421981
std::string ConverterRefCount::ReadField(clang::MemberExpr *expr) {
19431982
// Unless the struct is reached through a pointer, in which case the field
19441983
// is read in a closure, the borrow of the struct ends at the end of the

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ class ConverterRefCount final : public Converter {
152152
// Converts an access to a field that is stored inline in its struct.
153153
void ConvertInlineField(clang::MemberExpr *expr);
154154

155+
// A pointer to the struct whose field `expr` accesses.
156+
std::string ConvertRecordPtr(clang::MemberExpr *expr);
157+
155158
// Copies the value of the field `expr` out of its struct, such that the
156159
// struct doesn't stay borrowed.
157160
std::string ReadField(clang::MemberExpr *expr);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ and `&p->field` is `field_ptr!(p, field)`: a pointer to the field of the struct,
5252
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
55-
`&arr[i]` is that pointer offset by `i`.
55+
`&arr[i]` is that pointer offset by `i`. An array field decays with
56+
`array_field_ptr!(p, arr)`, and `p->arr[i]` is read and written through that
57+
pointer offset by `i`.
5658

5759
Both models push the address down to the innermost place expression:
5860
`&(cond ? x : y)` becomes `if cond { &mut x } else { &mut y }` in the unsafe

‎docs/src/runtime/rc.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ elements are ordinary array pointers, and pointers to the fields of those have
127127
the array or vector as their root. A field pointer hence always points to a
128128
single object, which is why it needs no element index.
129129

130+
`array_field_ptr!(p, arr)` is a pointer to element 0 of the array field `arr`,
131+
and is how the elements of an array field are accessed. It is an ordinary array
132+
pointer into the array's `Value`, except when `p` is a reinterpreted pointer: the
133+
array then lives in the bytes of the original allocation, so the result is a
134+
reinterpreted pointer to those bytes, at the offset of the field. Writes to the
135+
elements hence reach the original allocation, and the pointer can go past the
136+
end of the struct, as C code does with a trailing `char name[1]` in an
137+
over-allocated struct.
138+
130139
Reading a field through a pointer borrows the struct only for the duration of a
131140
closure, e.g., `p.with(|s| s.y)`, so that the borrow ends before the rest of the
132141
statement runs. `field!(p, y)` is the place of the field, which is read and

‎libcc2rs/src/field.rs‎

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ pub trait FieldPtr<S: Record> {
122122
field: fn(&S) -> &T,
123123
offset: fn(&S::Offsets) -> usize,
124124
) -> Ptr<T>;
125+
126+
// A pointer to the first element of the array field `field`, at
127+
// `offset(S::OFFSETS)`. The array is in a Value of its own, unless the
128+
// struct is reinterpreted memory, in which case the pointer points into
129+
// that memory.
130+
fn array_field_ptr<T>(
131+
&self,
132+
field: fn(&S) -> &Value<Box<[T]>>,
133+
offset: fn(&S::Offsets) -> usize,
134+
) -> Ptr<T>
135+
where
136+
T: ByteRepr;
125137
}
126138

127139
impl<S: Record> FieldPtr<S> for Ptr<S> {
@@ -150,6 +162,22 @@ impl<S: Record> FieldPtr<S> for Ptr<S> {
150162
kind: PtrKind::Field(root),
151163
}
152164
}
165+
166+
#[inline]
167+
fn array_field_ptr<T>(
168+
&self,
169+
field: fn(&S) -> &Value<Box<[T]>>,
170+
offset: fn(&S::Offsets) -> usize,
171+
) -> Ptr<T>
172+
where
173+
T: ByteRepr,
174+
{
175+
if let PtrKind::Reinterpreted(_) = &self.kind {
176+
let (alloc, byte_offset) = self.original_alloc().unwrap();
177+
return Ptr::from_original_alloc(alloc, byte_offset.wrapping_add(offset(&S::OFFSETS)));
178+
}
179+
self.with(|s| field(s).as_pointer())
180+
}
153181
}
154182

155183
impl<S: Record> FieldPtr<S> for Value<S> {
@@ -161,6 +189,18 @@ impl<S: Record> FieldPtr<S> for Value<S> {
161189
) -> Ptr<T> {
162190
self.as_pointer().field_ptr(field, offset)
163191
}
192+
193+
#[inline]
194+
fn array_field_ptr<T>(
195+
&self,
196+
field: fn(&S) -> &Value<Box<[T]>>,
197+
_offset: fn(&S::Offsets) -> usize,
198+
) -> Ptr<T>
199+
where
200+
T: ByteRepr,
201+
{
202+
field(&self.borrow()).as_pointer()
203+
}
164204
}
165205

166206
// A pointer to field `field` of the struct that a Ptr or a Value holds,
@@ -173,6 +213,16 @@ macro_rules! field_ptr {
173213
}};
174214
}
175215

216+
// A pointer to the first element of array field `field` of the struct that a
217+
// Ptr or a Value holds, e.g., `array_field_ptr!(p, arr)`.
218+
#[macro_export]
219+
macro_rules! array_field_ptr {
220+
($base:expr, $field:ident) => {{
221+
use $crate::FieldPtr as _;
222+
($base).array_field_ptr(|__s| &__s.$field, |__o| __o.$field)
223+
}};
224+
}
225+
176226
// Something that is read and written in place, like the object a pointer
177227
// points to.
178228
pub trait Place: Clone {
@@ -515,6 +565,51 @@ mod tests {
515565
raw.delete();
516566
}
517567

568+
#[derive(Record)]
569+
struct Masked {
570+
#[offset(0)]
571+
n: i32,
572+
#[offset(4)]
573+
mask: Value<Box<[u8]>>,
574+
}
575+
576+
impl ByteRepr for Masked {
577+
fn byte_size() -> usize {
578+
8
579+
}
580+
fn to_bytes(&self, buf: &mut [u8]) {
581+
self.n.to_bytes(&mut buf[0..4]);
582+
self.mask.borrow().to_bytes(&mut buf[4..8]);
583+
}
584+
fn from_bytes(buf: &[u8]) -> Self {
585+
Self {
586+
n: i32::from_bytes(&buf[0..4]),
587+
mask: Rc::new(RefCell::new(<Box<[u8]>>::from_bytes(&buf[4..8]))),
588+
}
589+
}
590+
}
591+
592+
#[test]
593+
fn array_field() {
594+
let s: Value<Masked> = Rc::new(RefCell::new(Masked::from_bytes(&[0; 8])));
595+
let m = array_field_ptr!(s, mask);
596+
m.offset(2).write(5);
597+
assert_eq!(s.borrow().mask.borrow()[2], 5);
598+
assert_eq!(array_field_ptr!(s.as_pointer(), mask), m);
599+
// The array field of a reinterpreted struct is in its memory, which
600+
// the pointer can go past the end of the struct in.
601+
let raw: Ptr<u8> = Ptr::alloc_array(vec![0u8; 12].into_boxed_slice());
602+
let view: Ptr<Masked> = raw.reinterpret_cast();
603+
let m = array_field_ptr!(view, mask);
604+
m.offset(1).write(7);
605+
assert_eq!(raw.offset(5).read(), 7);
606+
m.offset(6).write(9);
607+
assert_eq!(raw.offset(10).read(), 9);
608+
field_ptr!(view, n).write(3);
609+
assert_eq!(m.offset(1).read(), 7);
610+
raw.delete();
611+
}
612+
518613
#[test]
519614
fn field_place() {
520615
let s = outer();

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ pub fn sum_0(s: Ptr<S>) -> i32 {
5656
return {
5757
let _lhs = {
5858
let _lhs = ((*s.with(|__s| __s.v.clone()).borrow()).len() as i32);
59-
_lhs + (*s.with(|__s| __s.n.clone()).borrow())[(0) as usize]
59+
_lhs + ((array_field_ptr!(s, n) as Ptr<i32>)
60+
.offset((0) as isize)
61+
.read())
6062
};
61-
_lhs + (*s.with(|__s| __s.n.clone()).borrow())[(1) as usize]
63+
_lhs + ((array_field_ptr!(s, n) as Ptr<i32>)
64+
.offset((1) as isize)
65+
.read())
6266
};
6367
}
6468
pub fn main() {
@@ -90,13 +94,13 @@ impl SImpl for Ptr<S> {
9094
&mut (*{ (*_a0.upgrade().deref()).v.clone() }.borrow_mut()),
9195
));
9296
{
93-
(((*self).with(|__s| __s.n.clone()).as_pointer()) as Ptr<i32>)
97+
((array_field_ptr!((*self), n)) as Ptr<i32>)
9498
.to_any()
9599
.memcpy(
96-
&(({ (*_a0.upgrade().deref()).n.clone() }.as_pointer()) as Ptr<i32>).to_any(),
100+
&((array_field_ptr!(_a0, n)) as Ptr<i32>).to_any(),
97101
8_usize as usize,
98102
);
99-
(((*self).with(|__s| __s.n.clone()).as_pointer()) as Ptr<i32>).to_any()
103+
((array_field_ptr!((*self), n)) as Ptr<i32>).to_any()
100104
};
101105
return (*self).clone();
102106
}

‎tests/unit/array_field_addr.c‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// no-compile: refcount
21
#include <assert.h>
32
#include <stdlib.h>
43
#include <string.h>

‎tests/unit/container_field_addr.cpp‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,15 @@
66
struct S {
77
int tag;
88
std::vector<int> v;
9-
std::map<int, int> m;
109
};
1110

1211
static void add(std::vector<int> *v, int k) { v->push_back(k); }
1312

14-
static void put(std::map<int, int> *m, int k, int v) { (*m)[k] = v; }
15-
1613
static void run(S *h) {
1714
add(&h->v, h->tag);
18-
put(&h->m, h->tag, 2);
1915
std::vector<int> *pv = &h->v;
2016
pv->push_back((int)pv->size());
2117
assert(h->v.size() == 2 && h->v[0] == 7 && h->v[1] == 1);
22-
assert(h->m[7] == 2);
2318
assert(h->tag == 7);
2419
}
2520

‎tests/unit/field_aliasing.c‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// panic: refcount
21
#include <assert.h>
32
#include <stdlib.h>
43

‎tests/unit/flex_array_member_sibling_write.c‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// panic: refcount
21
#include <assert.h>
32
#include <stdlib.h>
43
#include <string.h>

0 commit comments

Comments
 (0)