2020-10-31 05:21:45 +08:00
|
|
|
<!--===- 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
|
|
|
|
|
|
|
|
```eval_rst
|
|
|
|
.. 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
|
2020-12-08 06:46:24 +08:00
|
|
|
* whether the subroutine is elemental (necessarily also impure)
|
2020-10-31 05:21:45 +08:00
|
|
|
|
|
|
|
### 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.
|