Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ bool IsUnionArrayMember(const clang::Expr *base) {
return false;
}

clang::Expr *SubscriptedArray(clang::Expr *base) {
while (auto *sub = clang::dyn_cast<clang::ArraySubscriptExpr>(
base->IgnoreImplicit())) {
base = sub->getBase();
}
return base;
}

// TODO: this will be gone when the AST matcher lands
std::tuple<clang::MemberExpr *, clang::RecordDecl *, std::string>
ReplaceNonUniformLibcField(clang::MemberExpr *expr) {
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ bool IsInMainFile(const clang::Decl *decl);

bool IsUnionArrayMember(const clang::Expr *base);

clang::Expr *SubscriptedArray(clang::Expr *base);

std::tuple<clang::MemberExpr *, clang::RecordDecl *, std::string>
ReplaceNonUniformLibcField(clang::MemberExpr *expr);

Expand Down
191 changes: 72 additions & 119 deletions cpp2rust/converter/models/converter_refcount.cpp

Large diffs are not rendered by default.

25 changes: 6 additions & 19 deletions cpp2rust/converter/models/converter_refcount.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ class ConverterRefCount final : public Converter {
clang::DeclRefExpr *GetGlobalArrayRValue(clang::Expr *base);
void ConvertPointerSubscript(clang::ArraySubscriptExpr *expr) override;

bool IsPointerSubscript(const clang::ArraySubscriptExpr *expr) const;

void ConvertFunctionMain(const clang::FunctionDecl *decl,
const std::string_view main_function_name) override;

Expand Down Expand Up @@ -494,14 +496,6 @@ class ConverterRefCount final : public Converter {
}
};

struct PushUnboxedIfSimple {
ConverterRefCount &c;
PushUnboxedIfSimple(ConverterRefCount &c, std::string_view outer_type,
clang::QualType inner_type);

~PushUnboxedIfSimple() { c.conversion_kind_.pop_back(); }
};

std::string BoxType(std::string &&str) const;
std::string BoxValue(std::string &&str) const;

Expand All @@ -514,32 +508,25 @@ class ConverterRefCount final : public Converter {
// emit ptr.write(rhs), or by ConvertMappedMethodCall to emit
// ptr.with_mut(...).
struct PendingDeref {
PendingDeref(ComputedExprType &type, clang::ASTContext &ctx)
: type(type), ctx(ctx) {}
void set(std::string str, bool fresh, clang::Expr *expr = nullptr);
void set_unchecked(std::string str, bool fresh,
clang::Expr *expr = nullptr);
PendingDeref(ComputedExprType &type) : type(type) {}
void set(std::string str, bool fresh);
void set_unchecked(std::string str, bool fresh);
std::string take() {
auto result = std::move(value);
value.clear();
pointee_is_boxed = false;
ptr_is_fresh = false;
return result;
}
bool empty() const { return value.empty(); }
bool is_boxed() const { return pointee_is_boxed; }
bool is_fresh() const { return ptr_is_fresh; }
void assert_consumed() const {
assert(value.empty() && "pending_deref_ not consumed");
}

private:
bool compute_inner_boxed(clang::Expr *expr) const;
ComputedExprType &type;
clang::ASTContext &ctx;
std::string value;
bool pointee_is_boxed = false;
bool ptr_is_fresh = false;
} pending_deref_{computed_expr_type_, ctx_};
} pending_deref_{computed_expr_type_};
};
} // namespace cpp2rust
13 changes: 7 additions & 6 deletions docs/src/codegen/types/boxing.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ the copy on entry then lets the body treat parameters exactly like local
variables. The preamble skips reference parameters, which are a `Ptr<T>` and
never boxed.

Nested containers, library ones and arrays alike, box each level except the
innermost, so that every inner container can be borrowed and mutated on its own,
and a pointer can be taken to it. The boxing is written into the type rules
themselves: `std::vector<std::vector<int>>` maps to `Vec<Value<Vec<i32>>>`, and
the `carray` rules map `int a[2][2]` to `Box<[Value<Box<[i32]>>]>`, both before
the outer `Value<...>` of the declaration is added.
Nested containers, library ones and arrays alike, store their inner levels
inline: `std::vector<std::vector<int>>` maps to `Vec<Vec<i32>>`, and
`int a[2][2]` to `Box<[Box<[i32]>]>`, both inside the outer `Value<...>` of the
declaration. Copying a nested container with `clone` hence copies every level,
as C++ does. A pointer into an inner container is a `Nested` pointer (see
[Pointer kinds](../../runtime/rc.md#pointer-kinds)), which reaches the inner
container through the outer one.

Struct fields are stored inline, so that a whole struct is a single allocation,
and a pointer to a field records the struct's allocation and the field's byte
Expand Down
36 changes: 18 additions & 18 deletions docs/src/codegen/types/mappings.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
The table gives the spelling of each C++ type in both models, before any
refcount boxing. `T` stands for the translated inner type.

| C++ | Unsafe model | Refcount model |
| ------------------------------ | ----------------------------------------------- | --------------------------------------------------------- |
| `bool` | `bool` | `bool` |
| `int`, `unsigned long`, ... | `i32`, `u64`, ... (host width) | same |
| `float`, `double` | `f32`, `f64` | same |
| `char` | `libc::c_char` | `u8` |
| `size_t` and other typedefs | by type rule (`usize`), else desugared | same |
| `T[N]` | `[T; N]` | `Box<[T]>` |
| `T[]` | `[T]` | `Box<[T]>` |
| `struct S`, `enum E` | `S`, `E` | same |
| `T *`, `T &` | `*mut T`, `*const T` | [`Ptr<T>`](../../runtime/rc.md#values-and-pointers) |
| `Abstract *` | `*mut dyn Abstract` | [`PtrDyn<dyn Abstract>`](../../runtime/ptr-dyn.md) |
| `void *` | `*mut ::libc::c_void` | [`AnyPtr`](../../runtime/void.md) |
| `R (*)(A)` | `Option<unsafe fn(A) -> R>` | [`FnPtr<fn(A) -> R>`](../../runtime/fn-ptr.md) |
| `va_list` | [`VaList`](../../runtime/va-args.md) | [`VaList`](../../runtime/va-args.md) |
| lambda closure | `impl Fn(A) -> R` as a parameter, `_` elsewhere | same |
| `std::unique_ptr<T>` | by type rule (`Option<Box<T>>`) | by type rule (`Option<Value<T>>`) |
| `std::vector<T>` and other STL | by type rule (`Vec<T>`) | by type rule (`Vec<T>`, `Vec<Value<Vec<T>>>` when nested) |
| C++ | Unsafe model | Refcount model |
| ------------------------------ | ----------------------------------------------- | --------------------------------------------------- |
| `bool` | `bool` | `bool` |
| `int`, `unsigned long`, ... | `i32`, `u64`, ... (host width) | same |
| `float`, `double` | `f32`, `f64` | same |
| `char` | `libc::c_char` | `u8` |
| `size_t` and other typedefs | by type rule (`usize`), else desugared | same |
| `T[N]` | `[T; N]` | `Box<[T]>` |
| `T[]` | `[T]` | `Box<[T]>` |
| `struct S`, `enum E` | `S`, `E` | same |
| `T *`, `T &` | `*mut T`, `*const T` | [`Ptr<T>`](../../runtime/rc.md#values-and-pointers) |
| `Abstract *` | `*mut dyn Abstract` | [`PtrDyn<dyn Abstract>`](../../runtime/ptr-dyn.md) |
| `void *` | `*mut ::libc::c_void` | [`AnyPtr`](../../runtime/void.md) |
| `R (*)(A)` | `Option<unsafe fn(A) -> R>` | [`FnPtr<fn(A) -> R>`](../../runtime/fn-ptr.md) |
| `va_list` | [`VaList`](../../runtime/va-args.md) | [`VaList`](../../runtime/va-args.md) |
| lambda closure | `impl Fn(A) -> R` as a parameter, `_` elsewhere | same |
| `std::unique_ptr<T>` | by type rule (`Option<Box<T>>`) | by type rule (`Option<Value<T>>`) |
| `std::vector<T>` and other STL | by type rule (`Vec<T>`) | by type rule (`Vec<T>`) |

Other built-ins (`wchar_t`, `long double`, `char16_t`) are omitted. Rvalue
references (`T &&`) have no mapping of their own; they reach the converter only
Expand Down
9 changes: 4 additions & 5 deletions docs/src/codegen/types/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ The refcount model skips `Clone` altogether when the copy constructor is
deleted, and never derives `Copy`. It derives `Clone` for C structs and for
classes with an implicit or defaulted copy constructor, which copy each field
with its own `clone`, i.e., its C++ copy constructor. The exceptions are fields
that are or nest a `Value`, such as `std::vector<int>` (`Value<Vec<i32>>`, see
[Boxing](boxing.md)) or `std::vector<std::vector<int>>`
(`Value<Vec<Value<Vec<i32>>>>`), whose derived `clone` would share the `Value`s
instead of copying them; the generated `Clone` then translates the implicit copy
constructor, which copies them deeply.
that are a `Value`, such as `std::vector<int>` (`Value<Vec<i32>>`, see
[Boxing](boxing.md)), whose derived `clone` would share the `Value` instead of
copying it; the generated `Clone` then translates the implicit copy constructor,
which copies them deeply.

A user-defined copy constructor takes a `Ptr` to the source, while `clone` only
has `&self`. `clone` passes it a pointer to a shallow copy of `self`, built
Expand Down
14 changes: 0 additions & 14 deletions docs/src/rules/rewriting.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,6 @@ reference usages (`r.push_back(20)` with `std::vector<int> &r = *p`); both are
[translated as a `Ptr`](../codegen/pointers.md), and that `Ptr` is what
`with_mut` is called on.

When the pointee is itself a boxed value (`Value<T>`, i.e. `Rc<RefCell<T>>`),
the closure takes `&mut Value<T>` and an extra borrow is inserted. This is the
case for nested containers: the refcount model translates
`std::vector<std::vector<int>>` as `Vec<Value<Vec<i32>>>` so that each element
has interior mutability of its own, and a `Ptr` to an inner vector therefore
points at a `Value<Vec<i32>>`, not a `Vec<i32>`:

```rust
ptr.with_mut(|__v: &mut Value<Vec<i32>>| (*__v.borrow_mut()).push(20))
```

The closure type is built from the C++ argument's type, not the rule's declared
parameter type.

## The read-access counterpart

For read access the converter does not emit a closure. A pointer receiver whose
Expand Down
13 changes: 10 additions & 3 deletions docs/src/runtime/rc.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ A `Ptr<T>` knows what it points into:
- `StackArray` and `HeapArray`: a fixed-size array.
- `StackVec` and `HeapVec`: a growable buffer; `std::vector` contents and string
literals live in one.
- `Nested`: an inner vector or array of a nested container, such as a row of
`std::vector<std::vector<int>>` or of `int[2][3]`. The inner container is
stored inline in the outer one, so the pointer holds the pointer to it, as an
`Rc<dyn SliceParent<T>>`, and reaches it through the outer container on every
access.
- `Field`: a field of a struct (see [Pointers to fields](#pointers-to-fields)).
- `Reinterpreted`: a byte-level view produced by a cast (see
[Type Reinterpretation](./reinterpret.md)).
Expand Down Expand Up @@ -300,9 +305,11 @@ through every other pointer at once.

Where the code generator needs a different view of the same allocation, it does
not upgrade at all. `decay` turns a pointer to a whole `Vec<T>` or `Box<[T]>`
into a pointer to its first element by re-tagging the existing weak reference,
and `Ptr::to_dyn` (see [Virtual Classes](./ptr-dyn.md)) does the same for the
upcast to a trait object.
into a pointer to its first element by re-tagging the existing weak reference;
when the `Vec<T>` or `Box<[T]>` is itself an element of another container, it
instead allocates a `Nested` pointer that holds the pointer to it. `Ptr::to_dyn`
(see [Virtual Classes](./ptr-dyn.md)) re-tags the weak reference in the same way
for the upcast to a trait object.

## Arithmetic

Expand Down
4 changes: 4 additions & 0 deletions libcc2rs/src/cstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ impl<T: CChar> Ptr<T> {
let b = rc.borrow();
f(until_nul(&b[self.offset..]))
}
PtrKind::Nested(parent) => {
let root = parent.root();
f(until_nul(&parent.borrow_slice(&*root)[self.offset..]))
}
PtrKind::Field(root) => {
let root = crate::field::upgrade(root);
let b = Ptr::<T>::borrow_field(&*root, self.offset);
Expand Down
3 changes: 3 additions & 0 deletions libcc2rs/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ impl<S: Record> FieldPtr<S> for Ptr<S> {
PtrKind::StackArray(w) | PtrKind::HeapArray(w) => (w.clone(), start()),
PtrKind::StackVec(w) | PtrKind::HeapVec(w) => (w.clone(), start()),
PtrKind::Field(root) => (root.clone(), self.offset),
PtrKind::Nested(_) => {
panic!("pointers to fields of elements of nested containers are not supported")
}
PtrKind::Reinterpreted(_) => {
let (alloc, byte_offset) = self.original_alloc().unwrap();
return Ptr::from_original_alloc(alloc, byte_offset.wrapping_add(offset));
Expand Down
Loading
Loading