-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Description
Currently, we represent thin references and pointers with DW_TAG_pointer_type DIEs and fat pointers (slices and trait objects) as DW_TAG_struct DIEs with fields representing payload and metadata pointers. This is not ideal and with debuggers knowing about Rust, we can do better. The question is, what exactly do we want the representation for these kinds of types to look like.
Some things seem pretty straightforward to me:
- Rust references should be
DW_TAG_reference_typeDIEs. - Rust raw pointers should be
DW_TAG_pointer_typeDIEs.
But beyond that, there are some decisions to be made:
(1) How do we represent mutability?
The C++ version of DWARF represents a const pointer like const char * with three separate type entries:
0:
DW_TAG_base_type
DW_AT_name "char"
...
1:
DW_TAG_const_type
DW_AT_type: ref to <0>
2:
DW_TAG_pointer_type
DW_AT_type: ref to <1>
I think this is a bit verbose and I'm not sure it is entirely appropriate for Rust. Do we really have const and mut types? That is, does Rust have the concept of a mut i32 at the type level, for example? I mean there are mutable and immutable slots/memory locations and we have "mutable" and "shared" references, but those two things seem kind of different to me.
As an alternative to using DW_TAG_const_type for representing mutability, we could re-use the DW_AT_mutable attribute that is already defined in DWARF. In C++ DWARF it is used for mutable fields. We could use it for reference type and local variable DIEs:
0: // char
DW_TAG_base_type
DW_AT_name "char"
...
1: // &mut char
DW_TAG_reference_type
DW_AT_type: ref to <0>
DW_AT_mutable: true
2: // &char
DW_TAG_reference_type
DW_AT_type: ref to <0>
DW_AT_mutable: false // or just leave it off
3:
DW_TAG_variable
DW_AT_name: "foo"
DW_AT_type: ref to <0>
DW_AT_mutable: true
...
(2) How to represent fat pointers?
The pointer types in C/C++ DWARF don't have DW_TAG_member sub-DIEs, since they are always just values. Fat pointers in Rust are different: they have one field that is a pointer to the data, and another field that holds additional information, either the size of a slice or the pointer to a vtable. These need to be described somehow.
I see a few options:
- A fat-pointer type is described by a
DW_TAG_pointer_typeorDW_TAG_reference_typeDIE with two fields that are described byDW_TAG_membersub-DIEs, both having theDW_AT_artificialattribute. @tromey once suggested for slices that the field entries have no name and the debugger determines which is which by the type (the size is always an integer type, the data is always a pointer type). This could also be extended for trait objects, since the data pointer will always be a pointer to a trait and the vtable-pointer will always be something else. - Treat trait objects and slices differently. Have a new
DW_TAG_slice_typeDIE that follows the encoding above and borrow some other attributes for trait objects: aDW_AT_vtable_elem_locationattribute holds the offset of the vtable field within the fat-pointer value, and aDW_AT_object_pointerattribute does the same for the data pointer. This is distinctly not how these attributes are used in a C++ context but it would be a nice fit, I think. - Mix of the above with
DW_AT_object_pointerindicating data pointer field
Another questions is: Should fat-pointers (and thin pointers too, maybe) have a DW_AT_byte_size attribute that specifies their size explicitly?
cc @tromey, @Manishearth
See also #33073