add ghostcell podcast episode
[web.git] / personal / _posts / 2020-04-04-layout-debugging.md
1 ---
2 title: "Debugging rustc type layouts"
3 categories: rust
4 forum: https://internals.rust-lang.org/t/psa-debugging-rustc-type-layouts/12082
5 ---
6
7 This post is a "public service announcement" for people working on the guts of rustc.
8 I wish I had known about this a year ago, so I hope this post can make this feature more widely known.
9
10 <!-- MORE -->
11
12 When working with MIR in rustc, one key data structure that comes up a lot is [`Layout`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.Layout.html) (formerly `LayoutDetails`), usually paired up with a type in a `TyAndLayout` (formerly `TyLayout`).
13 This data structure describes everything that there is to know about how a type "looks like" in memory: size and alignment of the entire type, at which offset we can find which field, how enum variants are represented, which "niche" can be used in this type to optimize enums.
14
15 `Layout` is quite versatile and can be hard to interpret, and when debugging Miri I regularly have to know what exactly the `Layout` of a certain type looks like or what exactly some aspect of `Layout` actually *means* in practice.
16 While debugging MIR is easy via `rustc --emit mir` or the "MIR" button on the playground, debugging `Layout` was much more tedious.
17 But not any more. :)
18
19 All you have to do is enter the following code in [the playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=7abc008aed2669466d4ebe79ee7767fe):
20
21 {% highlight rust %}
22 #![feature(rustc_attrs)]
23
24 #[rustc_layout(debug)]
25 type T = (u8, u16);
26 {% endhighlight %}
27
28 The (permanently) unstable `rustc_layout` attribute [can now be used](https://github.com/rust-lang/rust/pull/69901) to dump some information about the type it is attached to (also works with `struct`/`enum`/`union` definitions).
29 In this case, it prints:
30
31 ```
32 error: layout_of((u8, u16)) = Layout {
33     fields: Arbitrary {
34         offsets: [
35             Size {
36                 raw: 0,
37             },
38             Size {
39                 raw: 2,
40             },
41         ],
42         memory_index: [
43             0,
44             1,
45         ],
46     },
47     variants: Single {
48         index: 0,
49     },
50     abi: ScalarPair(
51         Scalar {
52             value: Int(
53                 I8,
54                 false,
55             ),
56             valid_range: 0..=255,
57         },
58         Scalar {
59             value: Int(
60                 I16,
61                 false,
62             ),
63             valid_range: 0..=65535,
64         },
65     ),
66     largest_niche: None,
67     align: AbiAndPrefAlign {
68         abi: Align {
69             pow2: 1,
70         },
71         pref: Align {
72             pow2: 3,
73         },
74     },
75     size: Size {
76         raw: 4,
77     },
78 }
79 ```
80
81 That is quite a lot, but it contains all the key information about this type:
82 the fields are at offsets 0 and 2, the type has alignment 2 (but preferred alignment 8) and size 4.
83 We can also see that it uses the `ScalarPair` abi which is relevant for Miri and when passing data as arguments to another function.
84 To learn more about what all this information means, see [the `Layout` type docs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.Layout.html).
85
86 **Update:** After a suggestions by @jschievink, this can now also be used to print the [underlying type and layout](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=1de2bed0c0d0f9171bfb41969f5028fb) of named opaque types, which is particularly useful [for generators](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=de99ab78a4d77bceee6760021b19de7d). **/Update**
87
88 So the next time you work with `Layout` and wonder how exactly the niche gets represented, or whether an `enum` can have `ScalarPair` abi (hint: yes it can), you can easily look at a few examples to see how rustc thinks about this type internally.
89 This is basically the type-level equivalent of `--emit mir`.
90 I have wanted this since forever, so much that some time ago I wrote an awful hack for this based on rustc debug tracing.
91 Only very recently did I learn about the `rustc_layout` attribute and then I had to immediately extend it to support dumping all the information.
92 Now `Layout` can be debugged in the browser on the playground, which is so much more convenient. :D