Issue 161109.1: No way to describe Fortran derived types with deferred length components
| Author: | Jakub Jelinek |
|---|---|
| Champion: | Cary Coutant |
| Date submitted: | 2016-11-09 |
| Date revised: | 2026-07-23 |
| Date closed: | |
| Type: | Enhancement |
| Status: | Open |
| DWARF version: | 6 |
Original Issue Description
When the producer implements Fortran derived type components with deferred length, e.g.
type t
integer :: j
character(len=:), allocatable :: f
integer :: i
end type
like in C:
struct t
{
int j;
void *f;
int i;
int _f_length; // The string length *f points to.
};
the current DWARF doesn't really allow to express this, at least not in the type
DIEs and their children (basically, for each variable of such type one would have
to create a new distinct set of type DIEs where the DW_AT_string_length attribute
would use a particular location expression specific to the particular variable.
The problem is that the string length is not stored in the CHARACTER(len=:)
objects themselves, but is shared by all the elements of the array. The
DW_AT_string_length must appear on the DW_TAG_string_type DIE, so
DW_OP_push_object_address on it pushes the address of a particular array
element. Where one can evaluate the string length is the DW_TAG_array_type,
where one could DW_OP_push_object_address, and is used for DW_AT_data_location
etc. We could e.g. allow DW_AT_string_length on DW_AT_array_type and have some
form of DW_AT_string_length or some new attribute on DW_TAG_string_type say that
DW_AT_string_length location isn't provided here, but on the parent (or grand
parent etc.) DW_TAG_array_type. Or have some DW_OP_* that would push address
of the containing array object instead of the current object's address.
Analysis
The GCC Fortran Compiler
For a deferred-length string in GCC Fortran, the length of that string is stored outside the string object itself.
For a standalone string variable with deferred length, the GCC Fortran compiler generates a separate (artificial) variable to hold the length:
character (:), allocatable :: s
V1: DW_TAG_variable
DW_AT_name: s
DW_AT_type: [ref to ptr to string type (T1)]
DW_AT_location: [DW_OP_fbreg(-8)]
V2: DW_TAG_variable
DW_AT_name: .s
DW_AT_type: [ref to int type]
DW_AT_artificial: 1
DW_AT_location: [DW_OP_fbreg(-16)]
T1: DW_TAG_pointer_type
DW_AT_byte_size: 8
DW_AT_type: [ref to string type (T2)]
T2: DW_TAG_string_type
DW_AT_string_length: [ref to V2]
There are two problems with this DWARF output:
-
The variable
sis not truly a pointer. It should be a string type with a data location attribute, rather than a pointer to a string type. -
The string type (T2 in the example above) has a reference to the artificial
.slength variable, so that each such string variable would require a separate type in order for each to refer to its associated length variable.
The DWARF for this case should instead use a string type rather than a pointer type for T1:
T1: DW_TAG_string_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_string_length: [ref to V2]
The pointer is essentially an object descriptor for the string, whose only field is a pointer to the allocated string.
This solves the first problem but not the second. In order for
two string variables s1 and s2 to share a type, we’d need to
allow the string length attribute to be placed in each variable DIE
instead of in the type DIE.
If a string is a member of a derived type, its length is stored in a separate (compiler-generated) field in the type.
type dstring
character (len=:), allocatable :: s
end type
T1: DW_TAG_pointer_type
DW_AT_byte_size: 8
DW_AT_type: [ref to T2]
T2: DW_TAG_string_type
DW_AT_byte_size: 0
T3: DW_TAG_structure_type
DW_AT_name: dstring
DW_AT_byte_size: 16
DW_TAG_member
DW_AT_name: s
DW_AT_type: [ref to T1]
DW_AT_data_member_location: 0
DW_TAG_member
DW_AT_name: _s_length
DW_AT_type: [ref to int type]
DW_AT_data_member_location: 8
There are three problems with the DWARF output for this example:
-
As above, the member
sshould be a string type, not a pointer type. -
There is nothing in the DWARF that identifies the
_s_lengthmember as the string length fors. -
The
_s_lengthmember should be flagged withDW_AT_artificial.
For an array of strings, the elements all share the same length field, which is stored in the type containing the array. The GCC Fortran compiler generates the following DWARF:
type dstring
character (len=:), allocatable :: s (:)
end type
type (dstring) :: d
V3: DW_TAG_variable
DW_AT_name: d
DW_AT_type: [ref to T4]
DW_AT_location: [DW_OP_addr(d)]
T4: DW_TAG_structure_type
DW_AT_name: dstring
DW_AT_byte_size: 72
DW_TAG_member
DW_AT_name: s
DW_AT_type: [ref to T5]
DW_AT_data_member_location: 0
DW_TAG_member
DW_AT_name: _s_length
DW_AT_type: [ref to int type]
DW_AT_data_member_location: 64
T5: DW_TAG_structure_type
DW_AT_declaration: 1
There are two problems with the DWARF output for this example:
-
The
_s_lengthfield should be marked artificial. -
The type of the
sfield (T5) is a structure type that is left undefined. It should instead be an array type whose elements are strings.
The type T5 would need to look something like the following:
T5: DW_TAG_array_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_allocated: [DW_OP_push_object_location; DW_OP_deref; DW_OP_lit0; DW_OP_ne]
DW_AT_type: [ref to T6]
DW_TAG_subrange_type
DW_AT_type: [ref to int type]
DW_AT_lower_bound: [DW_OP_push_object_location; DW_OP_plus_uconst: 24; DW_OP_deref]
DW_AT_count: [DW_OP_push_object_location; DW_OP_plus_uconst: 32; DW_OP_deref]
T6: DW_TAG_string_type
DW_AT_string_length: ???
But we still have a problem where the string type DIE cannot describe
the location of the string length field, because DW_OP_push_object_location
will push the location of the string object rather than of the array object.
Other Fortran Compilers
The LLVM and Intel Fortran compilers both choose to represent a deferred-length allocatable string, whether a standalone variable or a member of a derived type, as a descriptor with two fields — a data pointer and a string length. The string type would look something like the following:
T7: DW_TAG_string_type
DW_AT_data_location: [DW_OP_push_object_location, DW_OP_deref]
DW_AT_string_length: [DW_OP_push_object_location, DW_OP_plus_uconst 0x8]
With this representation, the type DIE can be shared by all variables of this type.
For an array of strings, however, a single length field is shared by all strings of the array, as in the GCC case. The DWARF output from these compilers is something like the following:
T8: DW_TAG_array_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_allocated: [DW_OP_push_object_location; DW_OP_deref; DW_OP_lit0; DW_OP_ne]
DW_AT_type: [ref to T9]
DW_TAG_subrange_type
DW_AT_type: [ref to int type]
DW_AT_lower_bound: [DW_OP_push_object_location; DW_OP_plus_uconst: 24; DW_OP_deref]
DW_AT_count: [DW_OP_push_object_location; DW_OP_plus_uconst: 32; DW_OP_deref]
DW_AT_byte_stride: [DW_OP_push_object_location; DW_OP_plus_uconst: 40; DW_OP_deref]
T9: DW_TAG_string_type
DW_AT_string_length: [DW_OP_push_object_location; DW_OP_plus_uconst: 8]
As described above, the string length attribute for the string type T9 is not correct. It wants to push the address of the containing array object, where the common string length can be found at offset 8, but will instead push the address of the string object.
Compare with type T7 in the example above, where the
DW_OP_push_object_location is intended to push the location
of the string object. To handle this latter case correctly,
debuggers are forced to use a heuristic to decide which object’s
location should be pushed.
Solutions
String Scalars
For the GCC model, where the string length is stored as a separate variable
or field, we should allow the DW_AT_string_length attribute to appear
on variable or data member DIEs when the string length is not part of the
string object. Furthermore, to make it clear that the string length is
deferred to the variable or data member DIE, the type DIE should have
a DW_AT_string_length attribute using the flag form class.
With this simple change, a Fortran deferred-length allocatable string can be represented as follows:
character (:), allocatable :: s1, s2
V1: DW_TAG_variable
DW_AT_name: s1
DW_AT_type: [ref to T1]
DW_AT_location: [...]
DW_AT_string_length: [ref to V3]
V2: DW_TAG_variable
DW_AT_name: s2
DW_AT_type: [ref to T1]
DW_AT_location: [...]
DW_AT_string_length: [ref to V4]
V3: DW_TAG_variable
DW_AT_name: .s1
DW_AT_type: [ref to int type]
DW_AT_artificial: 1
DW_AT_location: [...]
V4: DW_TAG_variable
DW_AT_name: .s2
DW_AT_type: [ref to int type]
DW_AT_artificial: 1
DW_AT_location: [...]
T1: DW_TAG_string_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_string_length: [flag_present]
Multiple strings can now share the single type DIE at T1.
For string members of a derived type, the string length attribute can be attached to the data member DIE, referring to the artificial data member where the string length is stored:
type dstring
character (len=:), allocatable :: s1, s2
end type
T1: DW_TAG_string_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_string_length: [flag_present]
T2: DW_TAG_structure_type
DW_AT_name: dstring
DW_AT_byte_size: 32
M1: DW_TAG_member
DW_AT_name: s1
DW_AT_type: [ref to T1]
DW_AT_data_member_location: 0
DW_AT_string_length: [ref to M3]
M2: DW_TAG_member
DW_AT_name: s2
DW_AT_type: [ref to T1]
DW_AT_data_member_location: 8
DW_AT_string_length: [ref to M4]
M3: DW_TAG_member
DW_AT_name: _s1_length
DW_AT_artificial: 1
DW_AT_type: [ref to int type]
DW_AT_data_member_location: 16
M4: DW_TAG_member
DW_AT_name: _s2_length
DW_AT_artificial: 1
DW_AT_type: [ref to int type]
DW_AT_data_member_location: 24
For the LLVM and Intel compiler model, where the string length is stored as a second field in the object descriptor, the existing DWARF (as shown at T7 in the example in the previous section) is sufficient and no changes are necessary.
String Arrays
A deferred-length allocatable array of strings is represented in DWARF
as an array type whose elements are string types, but the string elements
of the array all share the same length, which is stored in the array
descriptor. The individual strings are stored in a contiguous block
of characters whose location is given by the array type’s
DW_AT_data_location attribute. The string type needs to inherit
the string length attribute from the containing array object,
so we propose to place the DW_AT_string_length attribute on the
DW_TAG_array_type DIE, and let the string type for the array elements
use the flag form to indicate that the attribute is provided elsewhere.
character (len=:), allocatable :: s (:)
T3: DW_TAG_array_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_allocated: [DW_OP_push_object_location; DW_OP_deref; DW_OP_lit0; DW_OP_ne]
DW_AT_type: [ref to T4]
DW_AT_string_length: [DW_OP_push_object_location; DW_OP_plus_uconst: 8]
DW_TAG_subrange_type
DW_AT_type: [ref to int type]
DW_AT_lower_bound: [DW_OP_push_object_location; DW_OP_plus_uconst: 24; DW_OP_deref]
DW_AT_count: [DW_OP_push_object_location; DW_OP_plus_uconst: 32; DW_OP_deref]
DW_AT_byte_stride: [DW_OP_push_object_location; DW_OP_plus_uconst: 40; DW_OP_deref]
T4: DW_TAG_string_type
DW_AT_string_length: [flag_present]
Alternative Approach for String Arrays
[NOTE: This is a suggested alternative approach, but is not currently part of this proposal.]
Alternatively, we could introduce a new “string array” type tag, which could simplify the above to something like the following:
T5: DW_TAG_string_array_type
DW_AT_data_location: [DW_OP_push_object_location; DW_OP_deref]
DW_AT_allocated: [DW_OP_push_object_location; DW_OP_deref; DW_OP_lit0; DW_OP_ne]
DW_AT_string_length: [DW_OP_push_object_location; DW_OP_plus_uconst: 8]
DW_TAG_subrange_type
DW_AT_type: [ref to int type]
DW_AT_lower_bound: [DW_OP_push_object_location; DW_OP_plus_uconst: 24; DW_OP_deref]
DW_AT_count: [DW_OP_push_object_location; DW_OP_plus_uconst: 32; DW_OP_deref]
DW_AT_byte_stride: [DW_OP_push_object_location; DW_OP_plus_uconst: 40; DW_OP_deref]
Proposal
We propose the following changes to the DWARF spec:
-
Allow the use of
DW_AT_string_lengthattributes onDW_TAG_variable,DW_TAG_formal_parameter,DW_TAG_member, andDW_TAG_array_typeDIEs. TheDW_AT_string_length_byte_sizeandDW_AT_string_length_bit_sizeattributes are also allowed whereverDW_AT_string_lengthis allowed. -
Allow the use of the flag form for
DW_AT_string_lengthon aDW_TAG_string_typeDIE to indicate that the string length attribute will be provided externally. -
Add examples to show the use of
DW_AT_string_lengthfor Fortran deferred-length strings.
In Section 2.2 “Attribute Types”, Table 2.2 “Attribute names”,
for DW_AT_string_length, change the second column to read
“String length of string type or object”.
In Section 5.1 “Data Object Entries”, add the following item to the end of the numbered list:
14. A
DW_AT_string_lengthattribute for a variable or formal parameter entry, as described in Section 6.11.
[NOTE: We could, for completeness, include the DW_AT_string_length_byte_size
and DW_AT_string_length_bit_size attributes here, but they are unlikely
to be used in this context. For data object entries where a
DW_AT_string_length attribute is required, the attribute
is expected to be a reference to another variable, which will have
its own type information.]
In Section 6.5 “Array Type Entries”, add the following paragraph just before the last paragraph (“Other attributes...”):
For an array whose elements are a string type, where the string elements all share a common size and the size is dynamic (e.g., a Fortran deferred-length array), the entry may have a
DW_AT_string_lengthattribute that will be inherited by the element type given by theDW_AT_typeattribute of the array type. The entry may also have aDW_AT_string_length_byte_sizeorDW_AT_string_length_bit_sizeattribute. (See Section 6.11.)
In Section 6.7.6 “Data Member Entries”, add the following paragraph just before the last (non-normative) paragraph (“For showing nested and packed...”):
For data member with a string type, where the size is dynamic and stored in a separate member of the containing type (e.g., a Fortran deferred-length array), the entry may have a
DW_AT_string_lengthattribute, as described in Section 6.11.
In Section 6.11 “String Type Entries”, change the sixth paragraph as follows:
The string type entry may also have a
DW_AT_string_lengthattribute whose value is:either(a) a reference (see Section 2.18) to another debugging information entry that provides the value of the length of the string;or(b) a location expression yielding the location where the length of the string is stored in the program; or (c) a flag that indicates the string length is provided by a containing array type, a separate variable, or a separate data member. If the string type is the element type of an array type, case (c) indicates that the containing array type has aDW_AT_string_lengthattribute that provides the string length for all elements of the array. Otherwise, case (c) indicates that each variable or data member of this type has aDW_AT_string_lengthattribute that provides the string length for that instance of the string type.If
theaDW_AT_string_lengthattribute is not present, the size of the string is assumed to be the amount of storage that is allocated for the string (as specified by theDW_AT_byte_sizeorDW_AT_bit_sizeattribute).
In Section 8.5.4 “Attribute Encodings”, Table 8.5, for
DW_AT_string_length, add the flag class.
In Appendix A “Attributes by Tag”, Table A.1, add DW_AT_string_length,
DW_AT_string_length_byte_size, and DW_AT_string_length_bit_size
to the following tags:
DW_TAG_array_typeDW_TAG_formal_parameterDW_TAG_memberDW_TAG_variable
In Appendix D.14 “String Type Examples”, add the following examples:
Consider a deferred-length string in Fortran, as declared in Figure D.X1 below, where the compiler represents the string as a descriptor with two fields — a pointer to the data, and a length.
Figure D.X1: Deferred-length string type example: source character (:), allocatable :: s1The DWARF in Figure D.X2 would describe this representation.
Figure D.X2: Deferred-length string type example: DWARF representation 1$: DW_TAG_variable DW_AT_name: s1 DW_AT_type: (reference to 2$) DW_AT_location: (...) 2$: DW_TAG_string_type DW_AT_byte_size: 8 DW_AT_data_location: (expression= DW_OP_push_object_location DW_OP_deref) DW_AT_string_length: (expression= DW_OP_push_object_location DW_OP_plus_uconst 4)If the compiler instead represents the string as two separate variables — one for the pointer to the allocated data, and a second for the length — the DWARF in Figure D.X3 could be used to describe the representation.
Figure D.X3: Deferred-length string type example: alternate DWARF representation 1$: DW_TAG_variable DW_AT_name: s1 DW_AT_type: (reference to 3$) DW_AT_location: (...) DW_AT_string_length: (reference to 2$) 2$: DW_TAG_variable DW_AT_name: s1_length DW_AT_artificial: 1 DW_AT_type: (reference to INTEGER) DW_AT_location: (...) 3$: DW_TAG_string_type DW_AT_byte_size: 4 DW_AT_data_location: (expression= DW_OP_push_object_location DW_OP_deref) DW_AT_string_length: (flag 1)A deferred-length array of strings in Fortran, as shown in Figure D.X4, would store the length of each element of the array as part of the array descriptor. The DWARF in Figure D.X5 would describe this representation.
Figure D.X4: Deferred-length array of string example: source character (len=:), allocatable :: a (:) Figure D.X5: Deferred-length array of string example: DWARF representation 1$: DW_TAG_variable DW_AT_name: a DW_AT_type: (reference to 2$) DW_AT_location: (...) 2$: DW_TAG_array_type DW_AT_type: (reference to 3$) DW_AT_byte_size: 20 DW_AT_data_location: (expression= DW_OP_push_object_location DW_OP_deref) DW_AT_allocated: (...) DW_AT_string_length: (expression= DW_OP_push_object_location DW_OP_plus_uconst 4) DW_TAG_subrange_type DW_AT_type: (reference to INTEGER) DW_AT_lower_bound: (...) DW_AT_count: (...) DW_AT_byte_stride: (...) 3$: DW_TAG_string_type DW_AT_string_length: (flag 1)
2016-12-06: Deferred to DWARF Version 6.
2026-07-23: Rewrote to include analysis of how several Fortran compilers represent deferred-length strings, and added proposed extensions to DWARF with examples.