aboutsummaryrefslogtreecommitdiff
path: root/flang/docs/RuntimeTypeInfo.md
blob: 8bd5551c6667875c5f95853cb70c432b0806ff41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
<!--===- docs/RuntimeTypeInfo.md 
  
   Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
   See https://llvm.org/LICENSE.txt for license information.
   SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  
-->

# The derived type runtime information table

```{contents}
---
local:
---
```

## Overview

Many operations on derived types must be implemented, or can be
implemented, with calls to the runtime support library rather than
directly with generated code.
Some operations might be initially implemented in the runtime library
and then reimplemented later in generated code for compelling
performance gains in optimized compilations.

The runtime library uses *derived type description* tables to represent
the relevant characteristics of derived types.
This note summarizes the requirements for these descriptions.

The semantics phase of the F18 frontend constructs derived type
descriptions from its scoped symbol table after name resolution
and semantic constraint checking have succeeded.
The lowering phase then transfers the tables to the static
read-only data section of the generated program by translating them into
initialized objects.
During execution, references to the tables occur by passing their addresses
as arguments to relevant runtime library APIs and as pointers in
the addenda of descriptors.

## Requirements

The following Fortran language features require, or may require, the use of
derived type descriptions in the runtime library.

### Components

The components of a derived type need to be described in component
order (7.4.7), but when there is a parent component, its components
can be described by reference to the description of the type of the
parent component.

The ordered component descriptions are needed to implement
* default initialization
* `ALLOCATE`, with and without `SOURCE=`
* intrinsic assignment of derived types with `ALLOCATABLE` and
  automatic components
* intrinsic I/O of derived type instances
* `NAMELIST` I/O of derived type instances
* "same type" tests

The characteristics of data components include their names, types,
offsets, bounds, cobounds, derived type descriptions when appropriate,
default component initializers, and flags for `ALLOCATABLE`, `POINTER`,
`PRIVATE`, and automatic components (implicit allocatables).
Procedure pointer components require only their offsets and address(es).

### Calls to type-bound procedures

Only extensible derived types -- those without `SEQUENCE` or `BIND(C)`
-- are allowed to have type-bound procedures.
Calls to these bindings will be resolved at compilation time when
the binding is `NON_OVERRIDABLE` or when an object is not polymorphic.
Calls to overridable bindings of polymorphic objects requires the
use of a runtime table of procedure addresses.

Each derived type (or instantiation of a parameterized derived type)
will have a complete type-bound procedure table in which all of the
bindings of its ancestor types appear first.
(Specifically, the table offsets of any inherited bindings must be
the same as they are in the table of the ancestral type's table.)
These ancestral bindings reflect their overrides, if any.

The non-inherited bindings of a type then follow the inherited
bindings, and they do so in alphabetical order of binding name.
(This is an arbitrary choice -- we could also define them to
appear in binding declaration order, I suppose -- but a consistent
ordering should be used so that relocatables generated by distinct
versions of the F18 compiler will have a better chance to interoperate.)

### Type parameter values and "same type" testing

The values of the `KIND` and `LEN` parameters of a particular derived type
instance can be obtained to implement type parameter inquiries without
requiring derived type information tables.
In the case of a `KIND` type parameter, it's a constant value known at
compilation time, and in the case of a `LEN` type parameter, it's a
member of the addendum to the object's descriptor.

The runtime library will have an API (TBD) to be called as
part of the implementation of `TYPE IS` and `CLASS IS` guards
of the `SELECT TYPE` construct.
This language support predicate returns a true result when
an object's type matches a particular type specification and
`KIND` (but not `LEN`) type parameter values.

Note that this "is same type as" predicate is *not* the same as
the one to be called to implement the `SAME_TYPE_AS()` intrinsic function,
which is specified so as to *ignore* the values of `KIND` type
parameters.

Subclause 7.5.2 defines what being the "same" derived type means
in Fortran.
In short, each definition of a derived type defines a distinct type,
so type equality testing can usually compare addresses of derived
type descriptions at runtime.
The exceptions are `SEQUENCE` types and interoperable (`BIND(C)`)
types.
Independent definitions of each of these are considered to be the "same type"
when these definitions match in terms of names, types, and attributes,
both being either `SEQUENCE` or `BIND(C)`, and containing
no `PRIVATE` components.
These "sequence" derived types cannot have type parameters, type-bound
procedures, an absence of components, or components that are not themselves
of a sequence type, so we can use a static hash code to implement
their "same type" tests.

### FINAL subroutines

When an instance of a derived type is deallocated or goes out of scope,
one of its `FINAL` subroutines may be called.
Subclause 7.5.6.3 defines when finalization occurs -- it doesn't happen
in all situations.

The subroutines named in a derived type's `FINAL` statements are not
bindings, so their arguments are not passed object dummy arguments and
do not have to satisfy the constraints of a passed object.
Specifically, they can be arrays, and cannot be polymorphic.
If a `FINAL` subroutine's dummy argument is an array, it may be
assumed-shape or assumed-rank, but it could also be an explicit-shape
or assumed-size argument.
This means that it may or may not be passed by means of a descriptor.

Note that a `FINAL` subroutine with a scalar argument does not define
a finalizer for array objects unless the subroutine is elemental
(and probably `IMPURE`).
This seems to be a language pitfall and F18 will emit a
warning when an array of a finalizable derived type is declared
with a rank lacking a `FINAL` subroutine when other ranks do have one.

So the necessary information in the derived type table for a `FINAL`
subroutine comprises:
* address(es) of the subroutine
* rank of the argument, or whether it is assumed-rank
* for rank 0, whether the subroutine is elemental
* for rank > 0, whether the argument requires a descriptor

This descriptor flag is needed to handle a difficult case with
`FINAL` subroutines that most other implementations of Fortran
fail to get right: a `FINAL` subroutine
whose argument is a an explicit shape or assumed size array may
have to be called upon the parent component of an array of
an extended derived type.

```
  module m
    type :: parent
      integer :: n
     contains
      final :: subr
    end type
    type, extends(parent) :: extended
      integer :: m
    end type
   contains
    subroutine subr(a)
      type(parent) :: a(1)
    end subroutine
  end module
  subroutine demo
    use m
    type(extended) :: arr(1)
  end subroutine
```

If the `FINAL` subroutine doesn't use a descriptor -- and it
will not if there are no `LEN` type parameters -- the runtime
will have to allocate and populate a temporary array of copies
elements of the parent component of the array so that it can
be passed by reference to the `FINAL` subroutine.

### Defined assignment

A defined assignment subroutine for a derived type can be declared
by means of a generic `INTERFACE ASSIGNMENT(=)` and by means of
a generic type-bound procedure.
Defined assignments with non-type-bound generic interfaces are
resolved to specific subroutines at compilation time.
Most cases of type-bound defined assignment are resolved to their
bindings at compilation time as well (with possible runtime
resolution of overridable bindings).

Intrinsic assignment of derived types with components that have
derived types with type-bound generic assignments is specified
by subclause 10.2.1.3 paragraph 13 as invoking defined assignment
subroutines, however.

This seems to be the only case of defined assignment that may be of
interest to the runtime library.
If this is correct, then the requirements are somewhat constrained;
we know that the rank of the target of the assignment must match
the rank of the source, and that one of the dummy arguments of the
bound subroutine is a passed object dummy argument and satisfies
all of the constraints of one -- in particular, it's scalar and
polymorphic.

So the derived type information for a defined assignment needs to
comprise:
* address(es) of the subroutine
* whether the first, second, or both arguments are descriptors
* whether the subroutine is elemental (necessarily also impure)

### User defined derived type I/O

Fortran programs can specify subroutines that implement formatted and
unformatted `READ` and `WRITE` operations for derived types.
These defined I/O subroutines may be specified with an explicit `INTERFACE`
or with a type-bound generic.
When specified with an `INTERFACE`, the first argument must not be
polymorphic, but when specified with a type-bound generic, the first
argument is a passed-object dummy argument and required to be so.
In any case, the argument is scalar.

Nearly all invocations of user defined derived type I/O subroutines
are resolved at compilation time to specific procedures or to
overridable bindings.
(The I/O library APIs for acquiring their arguments remain to be
designed, however.)
The case that is of interest to the runtime library is that of
NAMELIST I/O, which is specified to invoke user defined derived
type I/O subroutines if they have been defined.

The derived type information for a user defined derived type I/O
subroutine comprises:
* address(es) of the subroutine
* whether it is for a read or a write
* whether it is formatted or unformatted
* whether the first argument is a descriptor (true if it is a
  binding of the derived type, or has a `LEN` type parameter)

## Exporting derived type descriptions from module relocatables

Subclause 7.5.2 requires that two objects be considered as having the
same derived type if they are declared "with reference to the same
derived type definition".
For derived types that are defined in modules and accessed by means
of use association, we need to be able to describe the type in the
read-only static data section of the module and access the description
as a link-time external.

This is not always possible to achieve in the case of instantiations
of parameterized derived types, however.
Two identical instantiations in distinct compilation units of the same
use associated parameterized derived type seem impractical to implement
using the same address.
(Perhaps some linkers would support unification of global objects
with "mangled" names and identical contents, but this seems unportable.)

Derived type descriptions therefore will contain pointers to
their "uninstantiated" original derived types.
For derived types with no `KIND` type parameters, these pointers
will be null; for uninstantiated derived types, these pointers
will point at themselves.