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-10-12 |
| Date closed: | 2026-10-26 |
| Type: | Enhancement |
| Status: | Accepted |
| DWARF version: | 6 |
Comparing original with latest. [ Return to the latest version ]
## 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 `s` is 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 `.s` length 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 `s` should be a string type, not a pointer type.
- There is nothing in the DWARF that identifies the `_s_length` member
as the string length for `s`.
- The `_s_length` member should be flagged with `DW_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_length` field should be marked artificial.
- The type of the `s` field (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,
[NOTE: This is now the suggested 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:
1. Add a new `DW_AT_string_array_type` for Fortran string arrays where every element of the array shares the same string length.
1. Allow the use of `DW_AT_string_length` attributes on `DW_TAG_variable`,
2. Allow the use of `DW_AT_string_length` attributes on `DW_TAG_variable`,
`DW_TAG_formal_parameter`, `DW_TAG_member`, and `DW_TAG_array_type` DIEs.
`DW_TAG_formal_parameter`, `DW_TAG_member`, and `DW_TAG_string_array_type` DIEs.
The `DW_AT_string_length_byte_size` and `DW_AT_string_length_bit_size` attributes are also allowed wherever `DW_AT_string_length` is allowed.
2. Allow the use of the *flag* form for `DW_AT_string_length` on a
3. Allow the use of the *flag* form for `DW_AT_string_length` on a
`DW_TAG_string_type` DIE to indicate that the string length attribute
will be provided externally.
is deferred to the instance of the type (e.g., a variable or formal parameter DIE).
3. Add examples to show the use of `DW_AT_string_length` for Fortran
4. Add examples to show the use of `DW_AT_string_length` for Fortran
deferred-length strings.
In Section 2.1 "The Debugging Information Entry (DIE)", Table 2.1 "Tag Names", add `DW_TAG_string_array_type`.
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_length` attribute 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_length` attribute that will be inherited > by the element type given by the `DW_AT_type` attribute of > the array type. > The entry may also have a `DW_AT_string_length_byte_size` > or `DW_AT_string_length_bit_size` attribute. > (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_length` attribute, 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_length` > attribute whose value is<ins>:</ins> <del>either</del> (a) a > reference (see Section 2.18) to another debugging information > entry that provides the value of the length of the > string<ins>;</ins> <del>or</del> (b) a location expression > yielding the location where the length of the string is stored in
> the program<ins>; or (c) a flag that indicates the string length
> the program<ins>; or (c) a flag that indicates that each > variable or data member of this type has a
> 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 a > `DW_AT_string_length` attribute that provides the string
> `DW_AT_string_length` attribute that provides the string length > for that instance of the string type.</ins>
> length for all elements of the array. > Otherwise, case (c) indicates that each variable or data member > of this type has a `DW_AT_string_length` attribute that > provides the string length for that instance of the string > type.</ins>
> If <del>the</del> <ins>a</ins> `DW_AT_string_length` > attribute 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 the `DW_AT_byte_size` or `DW_AT_bit_size` > attribute).
After Section 6.11 “String Type Entries”, add the following new section: > 6.12 String Array Type Entries > > *In Fortran, an array of strings declares an array of string elements, > all of which have a common size.* > > A string array type is represented by a debugging information entry > with the tag `DW_TAG_string_array_type`. It has the same attributes > and children as `DW_TAG_array_type` (see Section 6.5), with the > following exceptions: > > 1. A string array type entry may not have a `DW_AT_tensor` attribute. > > 2. The `DW_AT_type` entry, if present, refers to a type that describes > how each character of the string elements is encoded and interpreted, > as described in Section 6.11 for string type entries. > > 3. A string array type entry may also have any of the other attributes > of a string type, including `DW_AT_string_length`, as described > in Section 6.11 for string type entries. These attributes > describe the encoding of the string elements of the array. In Section 8.5.3 "Abbreviations Tables", Table 8.3 "Tag encodings", add a row for `DW_TAG_string_array_type` with a value TBA.
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_type` - `DW_TAG_formal_parameter` - `DW_TAG_member` - `DW_TAG_variable`
Add a new row for `DW_TAG_string_array_type` with the following applicable attributes: - DECL - `DW_AT_accessibility` - `DW_AT_alignment` - `DW_AT_allocated` - `DW_AT_associated` - `DW_AT_bit_size` - `DW_AT_bit_stride` - `DW_AT_byte_size` - `DW_AT_data_location` - `DW_AT_declaration` - `DW_AT_name` - `DW_AT_ordering` - `DW_AT_rank` - `DW_AT_specification` - `DW_AT_start_scope` - `DW_AT_string_length` - `DW_AT_string_length_bit_size` - `DW_AT_string_length_byte_size` - `DW_AT_type` - `DW_AT_visibility`
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 :: s1 > > The 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
> 2$: DW_TAG_string_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
> 3$: DW_TAG_base_type
> DW_AT_string_length: (flag 1)
> DW_AT_encoding: DW_ATE_ASCII
--- 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.
2026-10-12: [Rewrote][diff1] to use "Alternative Approach for String Arrays". 2026-10-26: Accepted.