9.4 Other data-oriented enhancements

9.4.1 Parameterised derived types [6.0 for kind type parameters, 6.1 for length]

Derived types may now have type parameters. Like those of the intrinsic types, they come in two flavours: "kind"-like ones which must be known at compile time (called “kind” type parameters), and ones like character length which may vary at runtime (called “length” type parameters).

9.4.1.1 Basic Syntax and Semantics

A derived type which has type parameters must list them in the type definition, give them a type, and specify whether they are “kind” or “length” parameters. For example,
  TYPE real_matrix(kind,n,m)
    INTEGER,KIND :: kind
    INTEGER(int64),LEN :: n,m

All type parameters must be explicitly specified to be of type INTEGER, but the kind of integer may vary. Type parameters are always scalar, never arrays. Within the type definition, “kind” type parameters may be used in constant expressions, and any type parameter may be used in a specification expression (viz array bound, character length, or “length” type parameter value). For example, the rest of the above type definition might look like:

    REAL(kind) value(n,m)
  END TYPE real_matrix

When declaring entities of such a derived type, the type parameters must be given after the name. For example,

  TYPE(real_matrix(KIND(0d0),100,200)) :: my_real_matrix_variable
Similarly, the type parameters must be given when constructing values of such a type; for example,
  my_real_matrix_variable = &
    real_matrix(kind(0d0),100,200)((/ (i*1.0d0,i=1,20000) /))

To examine the value of a derived type parameter from outside the type definition, the same notation is used as for component accesses, e.g.

  print *,'Columns =',my_real_matrix_variable%m
Thus type parameter names are in the same class as component names and type-bound procedure names. However, a type parameter cannot be changed by using its specifier on the left-hand-side of an assignment. Furthermore, the intrinsic type parameters may also be examined using this technique, for example:
  REAL :: array(:,:)
  CHARACTER(*),INTENT(IN) :: ch
  PRINT *,array%kind,ch%len
prints the same values as for KIND(array) and LEN(ch). Note that a kind parameter enquiry is always scalar, even if the object is an array.

A derived type parameter does not actually have to be used at all within the type definition, and a kind type parameter might only be used within specification expressions. For example,

  TYPE fixed_byte(n)
    INTEGER,KIND :: n
    INTEGER(1) :: value(n)
  END TYPE
  TYPE numbered_object(object_number)
    INTEGER,LEN :: object_number
  END TYPE
Even though the fixed_byte parameter n is not used in a constant expression, a constant value must always be specified for it because it has been declared to be a “kind” type parameter. Similarly, even though object_number has not been used at all, a value must always be specified for it. This is not quite as useless as it might seem: each numbered_object has a single value for object_number even if the numbered_object is an array.

9.4.1.2 More Semantics

A derived type with type parameters can have default values for one or more of them; in this case the parameters with default values may be omitted from the type specifiers. For example,
  TYPE char_with_maxlen(maxlen,kind)
    INTEGER,LEN :: maxlen = 254
    INTEGER,KIND   :: kind = SELECTED_CHAR_KIND('ascii')
    INTEGER        :: len = 0
    CHARACTER(len=maxlen,kind=kind) :: value
  END TYPE
  ...
  TYPE(char_with_maxlen) temp
  TYPE(char_with_maxlen(80)) card(1000)
  TYPE(char_with_maxlen(kind=SELECTED_CHAR_KIND('iso 10646'))) ucs4_temp

Note that although kind type parameters can be used in constant expressions and thus in default initialisation, components that are variable-sized (because they depend on length type parameters) cannot be default-initialised at all. Thus value in the example above cannot be default-initialised.

Further note that unlike intrinsic types, there are no automatic conversions for derived type assignment with different type parameter values, thus given the above declarations,

  card(1) = card(2) ! This is ok, maxlen==80 for both sides.
  temp = card       ! This is not allowed - maxlen 254 vs. maxlen 80.

9.4.1.3 Assumed type parameters

Assumed type parameters for derived types work similarly to character length, except that they are only allowed for dummy arguments (not for named constants). For example, the following subroutine works on any char_with_maxlen variable.
  SUBROUTINE stars(x)
    TYPE(char_with_maxlen(*)) x
    x%value = REPEAT('*',x%maxlen)
  END SUBROUTINE

9.4.1.4 Deferred type parameters

Deferred type parameters are completely new to Fortran 2003; these are available both for CHARACTER and for parameterised derived types, and work similarly to deferred array bounds. A variable with a deferred type parameter must have the ALLOCATABLE or POINTER attribute. The value of a deferred type parameter for an allocatable variable is that determined by allocation (either by a typed allocation, or by an intrinsic assignment with automatic reallocation). For a pointer, the value of a deferred type parameter is the value of the type parameter of its target. For example, using the type real_matrix defined above,
  TYPE(real_matrix(KIND(0.0),100,200)),TARGET :: x
  TYPE(real_matrix(KIND(0.0),:,:)),POINTER :: y, z
  ALLOCATE(real_matrix(KIND(0.0),33,44) :: y)  ! Typed allocation.
  z => x                                       ! Assumes from the target.
  PRINT *,y%n,z%n                              ! Prints 33 and 100.
Note that it is not allowed to reference the value of a deferred type parameter of an unallocated allocatable or of a pointer that is not associated with a target.

If a dummy argument is allocatable or a pointer, the actual argument must have deferred exactly the same type parameters as the dummy. For example,

  SUBROUTINE sub(rm_dble_ptr)
    TYPE(real_matrix(KIND(0d0),*,:)),POINTER :: rm_dble_ptr
  ...
  TYPE(real_matrix(KIND(0d0),100,200)),POINTER :: x
  TYPE(real_matrix(KIND(0d0),100,:)),POINTER :: y
  TYPE(real_matrix(KIND(0d0),:,:)),POINTER :: z
  CALL sub(x)  ! Invalid - X%M is not deferred (but must be).
  CALL sub(y)  ! This is ok.
  CALL sub(z)  ! Invalid - X%N is deferred (but must not be).

9.4.2 Finalisation [5.3]

An extensible derived type can have “final subroutines” associated with it; these subroutines are automatically called whenever an object of the type is about to be destroyed, whether by deallocation, procedure return, being on the left-hand-side of an intrinsic assignment, or being passed to an INTENT(OUT) dummy argument.

A final subroutine of a type must be a subroutine with exactly one argument, which must be an ordinary dummy variable of that type (and must not be INTENT(OUT)). It may be scalar or an array, and when an object of that type is destroyed the final subroutine whose argument has the same rank as the object is called. The final subroutine may be elemental, in which case it will handle any rank of object that has no other subroutine handling it. Note that if there is no final subroutine for the rank of an object, no subroutine will be called.

Final subroutines are declared in the type definition after the CONTAINS statement, like type-bound procedures. They are declared by a FINAL statement, which has the syntax

FINAL [ :: ] name [ , name ]...

where each name is a subroutine that satisfies the above rules.

A simple type with a final subroutine is as follows.

  TYPE flexible_real_vector
    LOGICAL :: value_was_allocated = .FALSE.
    REAL,POINTER :: value(:) => NULL()
  CONTAINS
    FINAL destroy_frv
  END TYPE
  ...
  ELEMENTAL SUBROUTINE destroy_frv(x)
    TYPE(flexible_real_vector),INTENT(INOUT) :: x
    IF (x%value_was_allocated) DEALLOCATE(x%value)
  END SUBROUTINE

If an object being destroyed has finalisable components, any final subroutine for the object-as-a-whole will be called before finalising any components. If the object is an array, each component will be finalised separately (and any final subroutine called will be the one for the rank of the component, not the rank of the object).

For example, in

  TYPE many_vectors
    TYPE(flexible_real_vector) scalar
    TYPE(flexible_real_vector) array(2,3)
  CONTAINS
    FINAL :: destroy_many_vectors_1
  END TYPE
  ...
  SUBROUTINE destroy_many_vectors_1(array1)
    TYPE(many_vectors) array1(:)
    PRINT *,'Destroying a',SIZE(array1),'element array of many vectors'
  END SUBROUTINE
  ...
  TYPE(many_vector) mv_object(3)
when mv_object is destroyed, firstly ‘destroy_many_vectors_1’ will be called with mv_object as its argument; this will print
 Destroying a 3 element array of many vectors
Secondly, for each element of the array, both the scalar and array components will be finalised by calling destroy_frv on each of them. These may be done in any order (or, since they are elemental, potentially in parallel).

Note that final subroutines are not inherited through type extension; instead, when an object of extended type is destroyed, first any final subroutine it has will be called, then any final subroutine of the parent type will be called on the parent component, and so on.

9.4.3 The PROTECTED attribute [5.0]

The PROTECTED attribute may be specified by the PROTECTED statement or with the PROTECTED keyword in a type declaration statement. It protects a module variable against modification from outside the module.

9.4.3.1 Syntax

The syntax of the PROTECTED statement is:

PROTECTED [ :: ] name [ , name ] ...

The PROTECTED attribute may only be specified for a variable in a module.

9.4.3.2 Semantics

Variables with the PROTECTED attribute may only be modified within the defining module. Outside of that module they are not allowed to appear in a variable definition context (e.g. on the left-hand-side of an assignment statement), similar to INTENT(IN) dummy arguments.

This allows the module writer to make the values of some variables generally available without relinquishing control over their modification.

9.4.3.3 Example

MODULE temperature_module
  REAL,PROTECTED :: temperature_c = 0, temperature_f = 32
CONTAINS
  SUBROUTINE set_temperature_c(new_value_c)
    REAL,INTENT(IN) :: new_value_c
    temperature_c = new_value_c
    temperature_f = temperature_c*(9.0/5.0) + 32
  END SUBROUTINE
  SUBROUTINE set_temperature_f(new_value_f)
    REAL,INTENT(IN) :: new_value_f
    temperature_f = new_value_f
    temperature_c = (temperature_f - 32)*(5.0/9.0)
  END SUBROUTINE
END
The PROTECTED attribute allows users of temperature_module to read the temperature in either Farenheit or Celsius, but the variables can only be changed via the provided subroutines which ensure that both values agree.

9.4.4 Pointer enhancements

9.4.4.1 INTENT for pointers [5.1]

A POINTER dummy argument may now have the INTENT attribute. This attribute applies to the pointer association status, not to the target of the pointer.

An INTENT(IN) pointer can be assigned to, but cannot be pointer-assigned, nullified, allocated or deallocated. An INTENT(OUT) pointer receives an undefined association status on entry to the procedure. An INTENT(INOUT) pointer has no restrictions on its use, but the actual argument must be a pointer variable, not a pointer function reference.

9.4.4.2 Pointer bounds specification [5.2]

The bounds of a pointer can be changed (from the default) in a pointer assignment by including them on the left-hand-side. For example,
  REAL,TARGET :: x(-100:100,-10:10)
  REAL,POINTER :: p(:,:)
  p(1:,1:) => x
The upper bound is formed by adding the extent (minus 1) to the lower bound, so in the above example, the bounds of P will be 1:201,1:21. Note that when setting the lower bound of any rank in a pointer assignment, the values must be explicitly specified (there is no default of 1 like there is in array declarators) and they must be specified for all dimensions of the pointer.

9.4.4.3 Rank-remapping Pointer Assignment [5.0]

This feature allows a multi-dimensional pointer to point to a single-dimensional object. For example:
  REAL,POINTER :: diagonal(:),matrix(:,:),base(:)
  ...
  ALLOCATE(base(n*n))
  matrix(1:n,1:n) => base
  diagonal => base(::n+1)
  !
  ! DIAGONAL now points to the diagonal elements of MATRIX.
  !

Note that when rank-remapping, the values for both the lower and upper bounds must be explicitly specified for all dimensions, there are no defaults.

9.4.5 Individual component accessibility [5.1]

It is now possible to set the accessibility of individual components in a derived type. For example,
  TYPE t
    LOGICAL, PUBLIC :: flag
    INTEGER, PRIVATE :: state
  END TYPE
The structure constructor for the type is not usable from outside the defining module if there is any private component that is not inherited, allocatable or default-initialised (see Structure constructor syntax enhancements).

9.4.6 Public entities of private type [5.1]

It is now possible to export entities (named constants, variables, procedures) from a module even if they have private type or (for procedures) have arguments of private type. For example,
  MODULE m
    TYPE, PRIVATE :: hidden_type
      CHARACTER(6) :: code
    END TYPE
    TYPE(hidden_type), PUBLIC, PARAMETER :: code_green = hidden_type('green')
    TYPE(hidden_type), PUBLIC, PARAMETER :: code_yellow = hidden_type('yellow')
    TYPE(hidden_type), PUBLIC, PARAMETER :: code_red = hidden_type('red')
  END