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 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:

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 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:

  1. 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. 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 DW_TAG_string_type DIE to indicate that the string length attribute will be provided externally.

  3. Add examples to show the use of DW_AT_string_length for 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_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: 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 a DW_AT_string_length attribute 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 a DW_AT_string_length attribute that provides the string length for that instance of the string type.

If the a 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).

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:

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
        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.