Skip to content

Commit 5265595

Browse files
authored
Add codegen/types docs (#370)
This doesn't have all the info updated. For example enums are no more transalted as enum in Rust, I plan to update that in a future PR. For the moment I would like to get the core of codegen/types merged, then I will make the appropiate edits.
1 parent 6485b0a commit 5265595

21 files changed

Lines changed: 1298 additions & 25 deletions

‎.github/workflows/docs.yml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ jobs:
1616
steps:
1717
- uses: actions/checkout@v7
1818
- name: Install mdbook
19-
run: cargo install mdbook
19+
run: cargo install mdbook mdbook-mermaid
20+
- name: Install mermaid assets
21+
run: mdbook-mermaid install docs
2022
- name: Build book
2123
run: mdbook build docs
2224
- uses: actions/upload-pages-artifact@v5

‎cpp2rust/converter/converter.cpp‎

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,24 +1525,6 @@ const clang::Expr *Converter::GetParentExpr(const clang::Expr *expr) {
15251525
return nullptr;
15261526
}
15271527

1528-
bool Converter::IsSubExprOf(const clang::Expr *sub_expr,
1529-
const clang::Expr *parent_expr) {
1530-
if (sub_expr == nullptr || parent_expr == nullptr)
1531-
return false;
1532-
1533-
if (parent_expr == sub_expr)
1534-
return true;
1535-
1536-
for (auto *child : parent_expr->children()) {
1537-
if (auto *child_expr = llvm::dyn_cast<clang::Expr>(child)) {
1538-
if (IsSubExprOf(sub_expr, child_expr))
1539-
return true;
1540-
}
1541-
}
1542-
1543-
return false;
1544-
}
1545-
15461528
bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt,
15471529
std::string &fmt_args, const char *&fmt_trait,
15481530
std::string &fmt_width) {

‎cpp2rust/converter/converter.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
442442

443443
protected:
444444
const clang::Expr *GetParentExpr(const clang::Expr *expr);
445-
bool IsSubExprOf(const clang::Expr *sub_expr, const clang::Expr *parent_expr);
446445

447446
#define StrCat(...) _StrCat(__FUNCTION__, __LINE__, __VA_ARGS__)
448447

‎docs/.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
book
2+
mermaid.min.js
3+
mermaid-init.js

‎docs/book.toml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ create-missing = false
1010
[output.html]
1111
git-repository-url = "https://github.com/cpp2rust/cpp2rust"
1212
edit-url-template = "https://github.com/cpp2rust/cpp2rust/edit/master/docs/{path}"
13+
additional-js = ["mermaid.min.js", "mermaid-init.js"]
1314

1415
[output.html.fold]
1516
enable = true
1617
level = 1
18+
19+
[preprocessor.mermaid]
20+
command = "mdbook-mermaid"

‎docs/src/SUMMARY.md‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,18 @@
4040
# Code Generation
4141

4242
- [Overview](./codegen/overview.md)
43-
- [Pointers and References](./codegen/pointers.md)
44-
- [Unions](./codegen/unions.md)
45-
- [Global Variables](./codegen/globals.md)
46-
- [Temporary Materialization](./codegen/temporaries.md)
47-
- [Translation Plugins](./codegen/plugins.md)
43+
- [The Translation Pipeline](./codegen/pipeline.md)
44+
- [Types](./codegen/types.md)
45+
- [Type Mappings](./codegen/types/mappings.md)
46+
- [Boxing](./codegen/types/boxing.md)
47+
- [Naming](./codegen/types/naming.md)
48+
- [Classes and Structs](./codegen/types/classes.md)
49+
- [Traits](./codegen/types/traits.md)
50+
- [Enums](./codegen/types/enums.md)
51+
- [Unions](./codegen/types/unions.md)
52+
- [Bit-fields](./codegen/types/bitfields.md)
53+
- [Pointers and References](./codegen/types/pointers.md)
54+
- [Casts](./codegen/types/casts.md)
55+
- [Function Pointers](./codegen/types/fn-pointers.md)
56+
- [Lambdas](./codegen/types/lambdas.md)
57+
- [Special-cased Library Types](./codegen/types/special-types.md)

‎docs/src/codegen/pipeline.md‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# The Translation Pipeline
2+
3+
<style>pre.mermaid { text-align: center; }</style>
4+
5+
```mermaid
6+
flowchart TD
7+
driver["<b>cpp2rust</b><br/><code>cpp2rust/cpp2rust.cpp</code>"]
8+
lib["<b>TranspileSrc / TranspileDir</b><br/><code>cpp2rust/cpp2rust_lib.cpp</code>"]
9+
action["<b>FrontendAction, ASTConsumer</b><br/><code>cpp2rust/ast_consumer.cpp</code>"]
10+
factory["<b>CreateConverter</b><br/><code>cpp2rust/converter/factory.cpp</code>"]
11+
mapper["<b>Mapper::LoadTranslationRules</b><br/><code>cpp2rust/converter/mapper.cpp</code>"]
12+
conv["<b>Converter / ConverterRefCount</b><br/><code>cpp2rust/converter/</code>"]
13+
out["<b>output file</b><br/>rustfmt"]
14+
driver -->|"source or compilation database"| lib
15+
lib -->|"one per translation unit"| action
16+
action --> factory
17+
factory -.->|"first call only"| mapper
18+
factory --> conv
19+
conv -->|"rs_code"| out
20+
```
21+
22+
## The stages
23+
24+
1. `cpp2rust` (`cpp2rust/cpp2rust.cpp`) parses the flags, resolves the rules
25+
directory (see
26+
[Loading and Matching](../rules/loading.md#finding-the-rules-directory)), and
27+
calls `TranspileSrc` for `--file` or `TranspileDir` for `--dir`.
28+
2. `TranspileSrc` / `TranspileDir` (`cpp2rust/cpp2rust_lib.cpp`) run clang
29+
tooling over the source or over every file in `compile_commands.json`, with
30+
one `FrontendAction` per translation unit.
31+
3. `ASTConsumer::HandleTranslationUnit` calls `CreateConverter`
32+
(`cpp2rust/converter/factory.cpp`), which loads the translation rules on its
33+
first call and constructs a `Converter` (`--model=unsafe`) or a
34+
`ConverterRefCount` (`--model=refcount`).
35+
4. The converter emits the file preamble if this is the first unit, then
36+
traverses the unit and appends Rust text to `rs_code`.
37+
5. The driver writes `rs_code` to the `-o` path and runs `rustfmt` on it.
38+
39+
## Gotchas
40+
41+
- Every unit is parsed with the platform flags from
42+
`cpp2rust/compat/platform_flags.h`, which put the
43+
[compat headers](../rules/compat.md) ahead of the system headers and set
44+
`-D_FORTIFY_SOURCE=0`, so macro-heavy libc APIs reach the converter as plain
45+
function calls.
46+
- In `--dir` mode `__FILE__` is redefined to the file's basename, so the
47+
generated code does not embed the absolute paths of the build machine.
48+
- Rules are loaded once per process, and the file preamble is emitted once, by
49+
the first unit; the bookkeeping that spans units (which declarations and
50+
records have already been emitted) is kept in `static` members of `Converter`.
51+
- After the last unit, `Converter::EmitOpaqueRecords` appends `pub struct Name;`
52+
for every record type that was referenced but never defined, so types only
53+
used behind pointers still compile.
54+
- A failing `rustfmt` is reported as an error, but the unformatted file stays on
55+
disk for inspection.

‎docs/src/codegen/types.md‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Types
2+
3+
Every place the converter prints a type goes through `Convert(QualType)`. It
4+
first asks the [type rules](../rules/writing-rules.md) for a mapping, so library
5+
types and typedef names such as `size_t` are resolved by rules, and only falls
6+
back to the `Visit*Type` methods for the built-in and user-defined types
7+
described here.
8+
9+
Given
10+
11+
```cpp
12+
struct Item {
13+
int id;
14+
char name[8];
15+
std::vector<int> refs;
16+
};
17+
18+
int count(Item item) { return item.id; }
19+
```
20+
21+
the unsafe model produces (attributes and trait impls omitted)
22+
23+
```rust
24+
pub struct Item {
25+
pub id: i32,
26+
pub name: [libc::c_char; 8],
27+
pub refs: Vec<i32>,
28+
}
29+
pub unsafe fn count_0(mut item: Item) -> i32 {
30+
return item.id;
31+
}
32+
```
33+
34+
and the refcount model produces
35+
36+
```rust
37+
pub struct Item {
38+
pub id: Value<i32>,
39+
pub name: Value<Box<[u8]>>,
40+
pub refs: Value<Vec<i32>>,
41+
}
42+
pub fn count_0(item: Item) -> i32 {
43+
let item: Value<Item> = Rc::new(RefCell::new(item));
44+
return *(*item.borrow()).id.borrow();
45+
}
46+
```
47+
48+
Every struct field is boxed in its own `Value<T>` so that a pointer can be taken
49+
to it. This is set to change: field writes and field addresses through a
50+
reinterpreted struct pointer go to a temporary and are lost, so fields will
51+
become plain `T` and `Ptr` will gain a kind that stores the parent struct plus
52+
an offset ([#309](https://github.com/Cpp2Rust/cpp2rust/issues/309)).
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Bit-fields
2+
3+
Bit-fields are not implemented. `VisitFieldDecl` ignores the declared width, so
4+
a field such as `unsigned flags : 3;` is emitted as a plain `u32` field: the
5+
struct compiles, but its layout, size, and the wrap-around of the field's value
6+
differ from C.
7+
8+
> [!WARNING]
9+
>
10+
> Code that relies on bit-field layout or width (packing several fields into one
11+
> word, `sizeof` on such a struct, storing a value wider than the field)
12+
> translates silently to something else
13+
> ([#312](https://github.com/Cpp2Rust/cpp2rust/issues/312)).

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Boxing
2+
3+
In the refcount model a variable is boxed: its type `T` is wrapped in
4+
`Value<T>`, an alias for `Rc<RefCell<T>>` (see
5+
[Reference Counting](../../runtime/rc.md)). Without the box, taking the address
6+
of a variable would need a Rust reference, and arbitrary C++ aliasing cannot be
7+
expressed with references.
8+
9+
Not every type position is boxed. `ConverterRefCount` keeps a stack of
10+
conversion kinds, [`conversion_kind_`](../internals/state.md), and the construct
11+
that owns the type pushes one before printing it:
12+
13+
- `FullRefCount`: pushed by variable and field declarations; `Convert(QualType)`
14+
wraps the result in `Value<...>`.
15+
- `Unboxed`: pushed by parameter lists, return types, and record names; the bare
16+
type is printed.
17+
- `Ptr`: pushed by a pointer type for its pointee; also printed bare.
18+
19+
The result by position:
20+
21+
| Position | `int` | `Item` | `int[3]` |
22+
| ------------------------------------------- | ------------ | ------------- | -------------------- |
23+
| local variable, struct field, global | `Value<i32>` | `Value<Item>` | `Value<Box<[i32]>>` |
24+
| function parameter, return type | `i32` | `Item` | decays to `Ptr<i32>` |
25+
| pointee of `Ptr<T>`, element of a container | `i32` | `Item` | `Box<[i32]>` |
26+
27+
Parameters arrive unboxed and are re-boxed by the function preamble; return
28+
values are unboxed:
29+
30+
```cpp
31+
int add(int a, Item item) { return a + item.id; }
32+
```
33+
34+
```rust
35+
pub fn add_0(a: i32, item: Item) -> i32 {
36+
let a: Value<i32> = Rc::new(RefCell::new(a));
37+
let item: Value<Item> = Rc::new(RefCell::new(item));
38+
return *a.borrow() + *(*item.borrow()).id.borrow();
39+
}
40+
```
41+
42+
C++ passes arguments to functions by copy, so signatures stay unboxed; boxing
43+
the copy on entry then lets the body treat parameters exactly like local
44+
variables. The preamble skips reference parameters, which are a `Ptr<T>` and
45+
never boxed.
46+
47+
Nested containers, library ones and arrays alike, box each level except the
48+
innermost, so that every inner container can be borrowed and mutated on its own,
49+
and a pointer can be taken to it. The boxing is written into the type rules
50+
themselves: `std::vector<std::vector<int>>` maps to `Vec<Value<Vec<i32>>>`, and
51+
the `carray` rules map `int a[2][2]` to `Box<[Value<Box<[i32]>>]>`, both before
52+
the outer `Value<...>` of the declaration is added.

0 commit comments

Comments
 (0)